mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Experience for KDA [Prep-work] (#1024)
* extensive modifications to source entry shorthand * moving 11 files changes 55 other files * added score card classes; upgraded packet classes * I decided to import over everything * proliferation of in-game activity messages, especially for spawning activity; removed defaults for activities; fixed (most?) building tests * upkeep on the LLU's managing service, as well as the facility hack management service, in response to a potential bug * a facility that changes faction affiliation while it is the destination of an LLU delivery will cancel that LLU delivery * fixed crash due to boomer trigger overriding position of ace, without the ace being properly cleaned up on the client of the bomber; fixed issue with the boomer trigger going missing * flipped the first two FDU deployable settings so they match the correct fire modes; corrected a stack overflow situation with the sourcing entities * action, but no response * condensed parameters on avatar class * as always, fixing tests * quickly, loose ends tied
This commit is contained in:
parent
40cf783f18
commit
779054fef9
|
|
@ -46,6 +46,7 @@ lazy val psforeverSettings = Seq(
|
||||||
"com.typesafe.akka" %% "akka-stream" % "2.6.17",
|
"com.typesafe.akka" %% "akka-stream" % "2.6.17",
|
||||||
"com.typesafe.akka" %% "akka-testkit" % "2.6.17" % "test",
|
"com.typesafe.akka" %% "akka-testkit" % "2.6.17" % "test",
|
||||||
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.17",
|
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.17",
|
||||||
|
"com.typesafe.akka" %% "akka-actor-testkit-typed" % "2.6.17" % "test",
|
||||||
"com.typesafe.akka" %% "akka-slf4j" % "2.6.17",
|
"com.typesafe.akka" %% "akka-slf4j" % "2.6.17",
|
||||||
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.17",
|
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.17",
|
||||||
"com.typesafe.akka" %% "akka-coordination" % "2.6.17",
|
"com.typesafe.akka" %% "akka-coordination" % "2.6.17",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import akka.testkit.TestProbe
|
||||||
import base.FreedContextActorTest
|
import base.FreedContextActorTest
|
||||||
import net.psforever.actors.zone.BuildingActor
|
import net.psforever.actors.zone.BuildingActor
|
||||||
import net.psforever.objects.avatar.Avatar
|
import net.psforever.objects.avatar.Avatar
|
||||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
import net.psforever.objects.ballistics.Projectile
|
||||||
import net.psforever.objects.guid.NumberPoolHub
|
import net.psforever.objects.guid.NumberPoolHub
|
||||||
import net.psforever.objects.guid.source.MaxNumberSource
|
import net.psforever.objects.guid.source.MaxNumberSource
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
|
|
@ -14,6 +14,7 @@ import net.psforever.objects.serverobject.deploy.Deployment
|
||||||
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
|
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl}
|
||||||
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
||||||
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.base.DamageResolution
|
import net.psforever.objects.vital.base.DamageResolution
|
||||||
import net.psforever.objects.vital.damage.DamageProfile
|
import net.psforever.objects.vital.damage.DamageProfile
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,12 @@ import base.FreedContextActorTest
|
||||||
import net.psforever.actors.commands.NtuCommand
|
import net.psforever.actors.commands.NtuCommand
|
||||||
import net.psforever.actors.zone.BuildingActor
|
import net.psforever.actors.zone.BuildingActor
|
||||||
import net.psforever.objects.avatar.Avatar
|
import net.psforever.objects.avatar.Avatar
|
||||||
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
import net.psforever.objects.ballistics.Projectile
|
||||||
import net.psforever.objects.guid.NumberPoolHub
|
import net.psforever.objects.guid.NumberPoolHub
|
||||||
import net.psforever.objects.guid.source.MaxNumberSource
|
import net.psforever.objects.guid.source.MaxNumberSource
|
||||||
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
import net.psforever.objects.serverobject.structures.{AutoRepairStats, Building, StructureType}
|
||||||
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
import net.psforever.objects.serverobject.terminals.{OrderTerminalDefinition, Terminal, TerminalControl}
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.base.DamageResolution
|
import net.psforever.objects.vital.base.DamageResolution
|
||||||
import net.psforever.objects.vital.damage.DamageProfile
|
import net.psforever.objects.vital.damage.DamageProfile
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,7 @@ class MiddlewareActor(
|
||||||
send(ServerStart(nonce, serverNonce), None, None)
|
send(ServerStart(nonce, serverNonce), None, None)
|
||||||
cryptoSetup()
|
cryptoSetup()
|
||||||
|
|
||||||
case (Unknown30(nonce), _) =>
|
case (Unknown30(_), _) =>
|
||||||
/*
|
/*
|
||||||
Unknown30 is used to reuse an existing crypto session when switching from login to world
|
Unknown30 is used to reuse an existing crypto session when switching from login to world
|
||||||
When not handling it, it appears that the client will fall back to using ClientStart
|
When not handling it, it appears that the client will fall back to using ClientStart
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,15 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
|
||||||
import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
|
import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
|
||||||
import net.psforever.objects.vital.{DamagingActivity, HealingActivity}
|
import net.psforever.objects.avatar.{ProgressDecoration, SpecialCarry}
|
||||||
|
import net.psforever.objects.avatar.scoring.{Death, EquipmentStat, KDAStat, Kill}
|
||||||
|
import net.psforever.objects.sourcing.SourceWithHealthEntry
|
||||||
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
|
import net.psforever.objects.vital.{DamagingActivity, HealingActivity, SpawningActivity, Vitality}
|
||||||
|
import net.psforever.packet.game.objectcreate.BasicCharacterData
|
||||||
|
import net.psforever.types.ExperienceType
|
||||||
import org.joda.time.{LocalDateTime, Seconds}
|
import org.joda.time.{LocalDateTime, Seconds}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.{ExecutionContextExecutor, Future, Promise}
|
import scala.concurrent.{ExecutionContextExecutor, Future, Promise}
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
@ -32,8 +39,8 @@ import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
import net.psforever.objects.inventory.InventoryItem
|
||||||
import net.psforever.objects.loadouts.{InfantryLoadout, Loadout, VehicleLoadout}
|
import net.psforever.objects.loadouts.{InfantryLoadout, Loadout, VehicleLoadout}
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ballistics.PlayerSource
|
|
||||||
import net.psforever.objects.locker.LockerContainer
|
import net.psforever.objects.locker.LockerContainer
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.objects.vital.HealFromImplant
|
import net.psforever.objects.vital.HealFromImplant
|
||||||
import net.psforever.packet.game.objectcreate.{ObjectClass, RibbonBars}
|
import net.psforever.packet.game.objectcreate.{ObjectClass, RibbonBars}
|
||||||
import net.psforever.packet.game.{Friend => GameFriend, _}
|
import net.psforever.packet.game.{Friend => GameFriend, _}
|
||||||
|
|
@ -188,7 +195,7 @@ object AvatarActor {
|
||||||
final case class SuspendStaminaRegeneration(duration: FiniteDuration) extends Command
|
final case class SuspendStaminaRegeneration(duration: FiniteDuration) extends Command
|
||||||
|
|
||||||
/** Award battle experience points */
|
/** Award battle experience points */
|
||||||
final case class AwardBep(bep: Long) extends Command
|
final case class AwardBep(bep: Long, modifier: ExperienceType) extends Command
|
||||||
|
|
||||||
/** Set total battle experience points */
|
/** Set total battle experience points */
|
||||||
final case class SetBep(bep: Long) extends Command
|
final case class SetBep(bep: Long) extends Command
|
||||||
|
|
@ -214,6 +221,8 @@ object AvatarActor {
|
||||||
|
|
||||||
final case class RemoveShortcut(slot: Int) extends Command
|
final case class RemoveShortcut(slot: Int) extends Command
|
||||||
|
|
||||||
|
final case class UpdateToolDischarge(stat: EquipmentStat) extends Command
|
||||||
|
|
||||||
final case class AvatarResponse(avatar: Avatar)
|
final case class AvatarResponse(avatar: Avatar)
|
||||||
|
|
||||||
final case class AvatarLoginResponse(avatar: Avatar)
|
final case class AvatarLoginResponse(avatar: Avatar)
|
||||||
|
|
@ -618,22 +627,28 @@ object AvatarActor {
|
||||||
*/
|
*/
|
||||||
def finalSavePlayerData(player: Player): Future[Int] = {
|
def finalSavePlayerData(player: Player): Future[Int] = {
|
||||||
val health = (
|
val health = (
|
||||||
player.History.find(_.isInstanceOf[DamagingActivity]),
|
player.History.findLast(_.isInstanceOf[DamagingActivity]),
|
||||||
player.History.find(_.isInstanceOf[HealingActivity])
|
player.History.collect { case h: HealingActivity => h }
|
||||||
) match {
|
) match {
|
||||||
case (Some(damage), Some(heal)) =>
|
case (Some(damage: DamagingActivity), heals) if heals.nonEmpty =>
|
||||||
|
val health = damage.data.targetAfter.asInstanceOf[PlayerSource].health
|
||||||
//between damage and potential healing, which came last?
|
//between damage and potential healing, which came last?
|
||||||
if (damage.time < heal.time) {
|
if (damage.time < heals.last.time) {
|
||||||
heal.asInstanceOf[HealingActivity].amount % player.MaxHealth
|
health + heals.map { _.amount }.sum
|
||||||
} else {
|
} else {
|
||||||
damage.asInstanceOf[DamagingActivity].data.targetAfter.asInstanceOf[PlayerSource].health
|
health
|
||||||
|
}
|
||||||
|
case (Some(damage: DamagingActivity), _) =>
|
||||||
|
damage.data.targetAfter.asInstanceOf[PlayerSource].health
|
||||||
|
case (None, heals) if heals.nonEmpty =>
|
||||||
|
player.History.headOption match {
|
||||||
|
case Some(es: SpawningActivity) =>
|
||||||
|
es.src.asInstanceOf[SourceWithHealthEntry].health + heals.map { _.amount }.sum
|
||||||
|
case _ =>
|
||||||
|
player.Health
|
||||||
}
|
}
|
||||||
case (Some(damage), None) =>
|
|
||||||
damage.asInstanceOf[DamagingActivity].data.targetAfter.asInstanceOf[PlayerSource].health
|
|
||||||
case (None, Some(heal)) =>
|
|
||||||
heal.asInstanceOf[HealingActivity].amount % player.MaxHealth
|
|
||||||
case _ =>
|
case _ =>
|
||||||
player.MaxHealth
|
player.Health
|
||||||
}
|
}
|
||||||
savePlayerData(player, health)
|
savePlayerData(player, health)
|
||||||
}
|
}
|
||||||
|
|
@ -772,6 +787,21 @@ object AvatarActor {
|
||||||
}
|
}
|
||||||
out.future
|
out.future
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def toAvatar(avatar: persistence.Avatar): Avatar =
|
||||||
|
Avatar(
|
||||||
|
avatar.id,
|
||||||
|
BasicCharacterData(
|
||||||
|
avatar.name,
|
||||||
|
PlanetSideEmpire(avatar.factionId),
|
||||||
|
CharacterSex.valuesToEntriesMap(avatar.genderId),
|
||||||
|
avatar.headId,
|
||||||
|
CharacterVoice(avatar.voiceId)
|
||||||
|
),
|
||||||
|
avatar.bep,
|
||||||
|
avatar.cep,
|
||||||
|
decoration = ProgressDecoration(cosmetics = avatar.cosmetics.map(c => Cosmetic.valuesFromObjectCreateValue(c)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AvatarActor(
|
class AvatarActor(
|
||||||
|
|
@ -937,7 +967,7 @@ class AvatarActor(
|
||||||
case Success(characters) =>
|
case Success(characters) =>
|
||||||
characters.headOption match {
|
characters.headOption match {
|
||||||
case Some(character) =>
|
case Some(character) =>
|
||||||
avatar = character.toAvatar
|
avatar = AvatarActor.toAvatar(character)
|
||||||
replyTo ! AvatarResponse(avatar)
|
replyTo ! AvatarResponse(avatar)
|
||||||
case None =>
|
case None =>
|
||||||
log.error(s"selected character $charId not found")
|
log.error(s"selected character $charId not found")
|
||||||
|
|
@ -1497,7 +1527,7 @@ class AvatarActor(
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val guid = player.GUID
|
val guid = player.GUID
|
||||||
val newHealth = player.Health = originalHealth + 1
|
val newHealth = player.Health = originalHealth + 1
|
||||||
player.History(HealFromImplant(PlayerSource(player), 1, implantType))
|
player.LogActivity(HealFromImplant(implantType, 1))
|
||||||
events ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(
|
||||||
zone.id,
|
zone.id,
|
||||||
AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth)
|
AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth)
|
||||||
|
|
@ -1595,67 +1625,24 @@ class AvatarActor(
|
||||||
initializeImplants()
|
initializeImplants()
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case AwardBep(bep) =>
|
case UpdateToolDischarge(stats) =>
|
||||||
context.self ! SetBep(avatar.bep + bep)
|
updateToolDischarge(stats)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
|
case AwardBep(bep, modifier) =>
|
||||||
|
setBep(avatar.bep + bep, modifier)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SetBep(bep) =>
|
case SetBep(bep) =>
|
||||||
import ctx._
|
setBep(bep, ExperienceType.Normal)
|
||||||
val result = for {
|
|
||||||
_ <-
|
|
||||||
if (BattleRank.withExperience(bep).value < BattleRank.BR24.value) setCosmetics(Set())
|
|
||||||
else Future.successful(())
|
|
||||||
r <- ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.bep -> lift(bep)))
|
|
||||||
} yield r
|
|
||||||
result.onComplete {
|
|
||||||
case Success(_) =>
|
|
||||||
sessionActor ! SessionActor.SendResponse(BattleExperienceMessage(session.get.player.GUID, bep, 0))
|
|
||||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
session.get.zone.id,
|
|
||||||
AvatarAction.PlanetsideAttributeToAll(session.get.player.GUID, 17, bep)
|
|
||||||
)
|
|
||||||
// when the level is reduced, take away any implants over the implant slot limit
|
|
||||||
val implants = avatar.implants.zipWithIndex.map {
|
|
||||||
case (implant, index) =>
|
|
||||||
if (index >= BattleRank.withExperience(bep).implantSlots && implant.isDefined) {
|
|
||||||
ctx.run(
|
|
||||||
query[persistence.Implant]
|
|
||||||
.filter(_.name == lift(implant.get.definition.Name))
|
|
||||||
.filter(_.avatarId == lift(avatar.id))
|
|
||||||
.delete
|
|
||||||
)
|
|
||||||
.onComplete {
|
|
||||||
case Success(_) =>
|
|
||||||
sessionActor ! SessionActor.SendResponse(
|
|
||||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, index, 0)
|
|
||||||
)
|
|
||||||
case Failure(exception) => log.error(exception)("db failure")
|
|
||||||
}
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
implant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
avatar = avatar.copy(bep = bep, implants = implants)
|
|
||||||
case Failure(exception) => log.error(exception)("db failure")
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case AwardCep(cep) =>
|
case AwardCep(cep) =>
|
||||||
context.self ! SetCep(avatar.cep + cep)
|
setCep(avatar.cep + cep)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SetCep(cep) =>
|
case SetCep(cep) =>
|
||||||
import ctx._
|
setCep(cep)
|
||||||
ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.cep -> lift(cep))).onComplete {
|
|
||||||
case Success(_) =>
|
|
||||||
avatar = avatar.copy(cep = cep)
|
|
||||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
session.get.zone.id,
|
|
||||||
AvatarAction.PlanetsideAttributeToAll(session.get.player.GUID, 18, cep)
|
|
||||||
)
|
|
||||||
case Failure(exception) => log.error(exception)("db failure")
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case SetCosmetics(cosmetics) =>
|
case SetCosmetics(cosmetics) =>
|
||||||
|
|
@ -1690,8 +1677,8 @@ class AvatarActor(
|
||||||
//short-circuit if the shortcut already exists at the given location
|
//short-circuit if the shortcut already exists at the given location
|
||||||
val isMacroShortcut = shortcut.isInstanceOf[Shortcut.Macro]
|
val isMacroShortcut = shortcut.isInstanceOf[Shortcut.Macro]
|
||||||
val isDifferentShortcut = !(targetShortcut match {
|
val isDifferentShortcut = !(targetShortcut match {
|
||||||
case Some(target) => AvatarShortcut.equals(shortcut, target)
|
case Some(target: AvatarShortcut) => AvatarShortcut.equals(shortcut, target)
|
||||||
case _ => false
|
case _ => false
|
||||||
})
|
})
|
||||||
if (isDifferentShortcut) {
|
if (isDifferentShortcut) {
|
||||||
if (!isMacroShortcut && avatar.shortcuts.flatten.exists {
|
if (!isMacroShortcut && avatar.shortcuts.flatten.exists {
|
||||||
|
|
@ -1701,7 +1688,7 @@ class AvatarActor(
|
||||||
if (shortcut.isInstanceOf[Shortcut.Implant]) {
|
if (shortcut.isInstanceOf[Shortcut.Implant]) {
|
||||||
//duplicate implant
|
//duplicate implant
|
||||||
targetShortcut match {
|
targetShortcut match {
|
||||||
case Some(existingShortcut) =>
|
case Some(existingShortcut: AvatarShortcut) =>
|
||||||
//redraw redundant shortcut slot with existing shortcut
|
//redraw redundant shortcut slot with existing shortcut
|
||||||
sessionActor ! SessionActor.SendResponse(
|
sessionActor ! SessionActor.SendResponse(
|
||||||
CreateShortcutMessage(session.get.player.GUID, slot + 1, Some(AvatarShortcut.convert(existingShortcut)))
|
CreateShortcutMessage(session.get.player.GUID, slot + 1, Some(AvatarShortcut.convert(existingShortcut)))
|
||||||
|
|
@ -2116,7 +2103,7 @@ class AvatarActor(
|
||||||
|
|
||||||
avatars.filter(!_.deleted) foreach { a =>
|
avatars.filter(!_.deleted) foreach { a =>
|
||||||
val secondsSinceLastLogin = Seconds.secondsBetween(a.lastLogin, LocalDateTime.now()).getSeconds
|
val secondsSinceLastLogin = Seconds.secondsBetween(a.lastLogin, LocalDateTime.now()).getSeconds
|
||||||
val avatar = a.toAvatar
|
val avatar = AvatarActor.toAvatar(a)
|
||||||
val player = new Player(avatar)
|
val player = new Player(avatar)
|
||||||
|
|
||||||
player.ExoSuit = ExoSuitType.Reinforced
|
player.ExoSuit = ExoSuitType.Reinforced
|
||||||
|
|
@ -2356,11 +2343,12 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
|
|
||||||
def refreshLoadouts(loadouts: Iterable[(Option[Loadout], Int)]): Unit = {
|
def refreshLoadouts(loadouts: Iterable[(Option[Loadout], Int)]): Unit = {
|
||||||
|
val guid = session.get.player.GUID
|
||||||
loadouts
|
loadouts
|
||||||
.map {
|
.map {
|
||||||
case (Some(loadout: InfantryLoadout), index) =>
|
case (Some(loadout: InfantryLoadout), index) =>
|
||||||
FavoritesMessage.Infantry(
|
FavoritesMessage.Infantry(
|
||||||
session.get.player.GUID,
|
guid,
|
||||||
index,
|
index,
|
||||||
loadout.label,
|
loadout.label,
|
||||||
InfantryLoadout.DetermineSubtypeB(loadout.exosuit, loadout.subtype)
|
InfantryLoadout.DetermineSubtypeB(loadout.exosuit, loadout.subtype)
|
||||||
|
|
@ -2368,14 +2356,14 @@ class AvatarActor(
|
||||||
case (Some(loadout: VehicleLoadout), index)
|
case (Some(loadout: VehicleLoadout), index)
|
||||||
if GlobalDefinitions.isBattleFrameVehicle(loadout.vehicle_definition) =>
|
if GlobalDefinitions.isBattleFrameVehicle(loadout.vehicle_definition) =>
|
||||||
FavoritesMessage.Battleframe(
|
FavoritesMessage.Battleframe(
|
||||||
session.get.player.GUID,
|
guid,
|
||||||
index - 15,
|
index - 15,
|
||||||
loadout.label,
|
loadout.label,
|
||||||
VehicleLoadout.DetermineBattleframeSubtype(loadout.vehicle_definition)
|
VehicleLoadout.DetermineBattleframeSubtype(loadout.vehicle_definition)
|
||||||
)
|
)
|
||||||
case (Some(loadout: VehicleLoadout), index) =>
|
case (Some(loadout: VehicleLoadout), index) =>
|
||||||
FavoritesMessage.Vehicle(
|
FavoritesMessage.Vehicle(
|
||||||
session.get.player.GUID,
|
guid,
|
||||||
index - 10,
|
index - 10,
|
||||||
loadout.label
|
loadout.label
|
||||||
)
|
)
|
||||||
|
|
@ -2389,7 +2377,7 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
FavoritesMessage(
|
FavoritesMessage(
|
||||||
mtype,
|
mtype,
|
||||||
session.get.player.GUID,
|
guid,
|
||||||
lineNo,
|
lineNo,
|
||||||
"",
|
"",
|
||||||
0
|
0
|
||||||
|
|
@ -2812,4 +2800,110 @@ class AvatarActor(
|
||||||
sessionActor ! SessionActor.UpdateIgnoredPlayers(FriendsResponse(MemberAction.RemoveIgnoredPlayer, GameFriend(name)))
|
sessionActor ! SessionActor.UpdateIgnoredPlayers(FriendsResponse(MemberAction.RemoveIgnoredPlayer, GameFriend(name)))
|
||||||
sessionActor ! SessionActor.CharSaved
|
sessionActor ! SessionActor.CharSaved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def setBep(bep: Long, modifier: ExperienceType): Unit = {
|
||||||
|
import ctx._
|
||||||
|
val result = for {
|
||||||
|
_ <-
|
||||||
|
if (BattleRank.withExperience(bep).value < BattleRank.BR24.value) setCosmetics(Set())
|
||||||
|
else Future.successful(())
|
||||||
|
r <- ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.bep -> lift(bep)))
|
||||||
|
} yield r
|
||||||
|
result.onComplete {
|
||||||
|
case Success(_) =>
|
||||||
|
val sess = session.get
|
||||||
|
val zone = sess.zone
|
||||||
|
val pguid = sess.player.GUID
|
||||||
|
val localModifier = modifier
|
||||||
|
sessionActor ! SessionActor.SendResponse(BattleExperienceMessage(pguid, bep, localModifier))
|
||||||
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
|
zone.id,
|
||||||
|
AvatarAction.PlanetsideAttributeToAll(pguid, 17, bep)
|
||||||
|
)
|
||||||
|
// when the level is reduced, take away any implants over the implant slot limit
|
||||||
|
val implants = avatar.implants.zipWithIndex.map {
|
||||||
|
case (implant, index) =>
|
||||||
|
if (index >= BattleRank.withExperience(bep).implantSlots && implant.isDefined) {
|
||||||
|
ctx.run(
|
||||||
|
query[persistence.Implant]
|
||||||
|
.filter(_.name == lift(implant.get.definition.Name))
|
||||||
|
.filter(_.avatarId == lift(avatar.id))
|
||||||
|
.delete
|
||||||
|
)
|
||||||
|
.onComplete {
|
||||||
|
case Success(_) =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(
|
||||||
|
AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0)
|
||||||
|
)
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
}
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
implant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avatar = avatar.copy(bep = bep, implants = implants)
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def setCep(cep: Long): Unit = {
|
||||||
|
import ctx._
|
||||||
|
ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.cep -> lift(cep))).onComplete {
|
||||||
|
case Success(_) =>
|
||||||
|
val sess = session.get
|
||||||
|
val zone = sess.zone
|
||||||
|
avatar = avatar.copy(cep = cep)
|
||||||
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
|
zone.id,
|
||||||
|
AvatarAction.PlanetsideAttributeToAll(sess.player.GUID, 18, cep)
|
||||||
|
)
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(exception)("db failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateKillsDeathsAssists(kdaStat: KDAStat): Unit = {
|
||||||
|
avatar.scorecard.rate(kdaStat)
|
||||||
|
val exp = kdaStat.experienceEarned
|
||||||
|
val _session = session.get
|
||||||
|
val zone = _session.zone
|
||||||
|
val player = _session.player
|
||||||
|
kdaStat match {
|
||||||
|
case kill: Kill =>
|
||||||
|
val _ = PlayerSource(player)
|
||||||
|
(kill.info.interaction.cause match {
|
||||||
|
case pr: ProjectileReason => pr.projectile.mounted_in.map { a => zone.GUID(a._1) }
|
||||||
|
case _ => None
|
||||||
|
}) match {
|
||||||
|
case Some(Some(_: Vitality)) =>
|
||||||
|
//zone.actor ! ZoneActor.RewardOurSupporters(playerSource, obj.History, kill, exp)
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
//zone.actor ! ZoneActor.RewardOurSupporters(playerSource, player.History, kill, exp)
|
||||||
|
case _: Death =>
|
||||||
|
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
|
player.Name,
|
||||||
|
AvatarAction.SendResponse(
|
||||||
|
Service.defaultPlayerGUID,
|
||||||
|
AvatarStatisticsMessage(DeathStatistic(avatar.scorecard.Lives.size))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (exp > 0L) {
|
||||||
|
val gameOpts = Config.app.game
|
||||||
|
val (msg, modifier): (ExperienceType, Float) = if (player.Carrying.contains(SpecialCarry.RabbitBall)) {
|
||||||
|
(ExperienceType.RabbitBall, 1.25f)
|
||||||
|
} else {
|
||||||
|
(ExperienceType.Normal, 1f)
|
||||||
|
}
|
||||||
|
setBep(avatar.bep + (exp * modifier * gameOpts.bepRate).toLong, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateToolDischarge(stats: EquipmentStat): Unit = {
|
||||||
|
avatar.scorecard.rate(stats)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import net.psforever.objects.definition.ImplantDefinition
|
||||||
import net.psforever.packet.game.{CreateShortcutMessage, Shortcut}
|
import net.psforever.packet.game.{CreateShortcutMessage, Shortcut}
|
||||||
import net.psforever.packet.game.objectcreate.DrawnSlot
|
import net.psforever.packet.game.objectcreate.DrawnSlot
|
||||||
import net.psforever.types.ChatMessageType.{CMT_GMOPEN, UNK_227}
|
import net.psforever.types.ChatMessageType.{CMT_GMOPEN, UNK_227}
|
||||||
import net.psforever.types.ImplantType
|
import net.psforever.types.{ExperienceType, ImplantType}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.ExecutionContextExecutor
|
import scala.concurrent.ExecutionContextExecutor
|
||||||
|
|
@ -820,7 +820,7 @@ class ChatActor(
|
||||||
|
|
||||||
case (CMT_ADDBATTLEEXPERIENCE, _, contents) if gmCommandAllowed =>
|
case (CMT_ADDBATTLEEXPERIENCE, _, contents) if gmCommandAllowed =>
|
||||||
contents.toIntOption match {
|
contents.toIntOption match {
|
||||||
case Some(bep) => avatarActor ! AvatarActor.AwardBep(bep)
|
case Some(bep) => avatarActor ! AvatarActor.AwardBep(bep, ExperienceType.Normal)
|
||||||
case None =>
|
case None =>
|
||||||
sessionActor ! SessionActor.SendResponse(
|
sessionActor ! SessionActor.SendResponse(
|
||||||
message.copy(messageType = UNK_229, contents = "@CMT_ADDBATTLEEXPERIENCE_usage")
|
message.copy(messageType = UNK_229, contents = "@CMT_ADDBATTLEEXPERIENCE_usage")
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ class SessionAvatarHandlers(
|
||||||
chatActor: typed.ActorRef[ChatActor.Command],
|
chatActor: typed.ActorRef[ChatActor.Command],
|
||||||
implicit val context: ActorContext
|
implicit val context: ActorContext
|
||||||
) extends CommonSessionInterfacingFunctionality {
|
) extends CommonSessionInterfacingFunctionality {
|
||||||
|
private[support] var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(elem=0L)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* na
|
* na
|
||||||
*
|
*
|
||||||
|
|
@ -117,7 +119,7 @@ class SessionAvatarHandlers(
|
||||||
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
|
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
|
||||||
// TODO Temporary thing that should go somewhere else and use proper xp values
|
// TODO Temporary thing that should go somewhere else and use proper xp values
|
||||||
if (killer.CharId == avatar.id && killer.Faction != victim.Faction) {
|
if (killer.CharId == avatar.id && killer.Faction != victim.Faction) {
|
||||||
avatarActor ! AvatarActor.AwardBep((1000 * Config.app.game.bepRate).toLong)
|
avatarActor ! AvatarActor.AwardBep((1000 * Config.app.game.bepRate).toLong, ExperienceType.Normal)
|
||||||
avatarActor ! AvatarActor.AwardCep((100 * Config.app.game.cepRate).toLong)
|
avatarActor ! AvatarActor.AwardCep((100 * Config.app.game.cepRate).toLong)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,7 +289,7 @@ class SessionAvatarHandlers(
|
||||||
val r2 = 2 + r.nextInt(4000).toFloat
|
val r2 = 2 + r.nextInt(4000).toFloat
|
||||||
(Vector3(r2, r2, r1), 0L, 0f)
|
(Vector3(r2, r2, r1), 0L, 0f)
|
||||||
} else {
|
} else {
|
||||||
val before = player.lastSeenStreamMessage(guid.guid)
|
val before = lastSeenStreamMessage(guid.guid)
|
||||||
val dist = Vector3.DistanceSquared(player.Position, pos)
|
val dist = Vector3.DistanceSquared(player.Position, pos)
|
||||||
(pos, now - before, dist)
|
(pos, now - before, dist)
|
||||||
}
|
}
|
||||||
|
|
@ -307,7 +309,7 @@ class SessionAvatarHandlers(
|
||||||
is_cloaking
|
is_cloaking
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
player.lastSeenStreamMessage(guid.guid) = now
|
lastSeenStreamMessage(guid.guid) = now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package net.psforever.actors.session.support
|
||||||
|
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
import akka.actor.{ActorContext, ActorRef, Cancellable, OneForOneStrategy, SupervisorStrategy, typed}
|
import akka.actor.{ActorContext, ActorRef, Cancellable, OneForOneStrategy, SupervisorStrategy, typed}
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
|
@ -940,18 +942,22 @@ class SessionData(
|
||||||
|
|
||||||
def handleFacilityBenefitShieldChargeRequest(pkt: FacilityBenefitShieldChargeRequestMessage): Unit = {
|
def handleFacilityBenefitShieldChargeRequest(pkt: FacilityBenefitShieldChargeRequestMessage): Unit = {
|
||||||
val FacilityBenefitShieldChargeRequestMessage(_) = pkt
|
val FacilityBenefitShieldChargeRequestMessage(_) = pkt
|
||||||
continent.GUID(player.VehicleSeated) match {
|
val vehicleGuid = player.VehicleSeated
|
||||||
case Some(obj) if obj.Destroyed => () //vehicle will try to charge even if destroyed
|
continent
|
||||||
case Some(obj: Vehicle) =>
|
.GUID(vehicleGuid)
|
||||||
obj.Actor ! Vehicle.ChargeShields(15)
|
.foreach {
|
||||||
case Some(_: TurretDeployable) => () //TODO the turret will charge a shield in some circumstances
|
case obj: Vehicle if !obj.Destroyed => //vehicle will try to charge even if destroyed
|
||||||
case None if player.VehicleSeated.nonEmpty =>
|
obj.Actor ! CommonMessages.ChargeShields(
|
||||||
log.error(
|
15,
|
||||||
s"FacilityBenefitShieldChargeRequest: ${player.Name} is seated in a vehicle that can not be found in zone ${continent.id}"
|
Some(continent.blockMap.sector(obj).buildingList.maxBy(_.Definition.SOIRadius))
|
||||||
)
|
)
|
||||||
case _ =>
|
case _ if vehicleGuid.nonEmpty =>
|
||||||
log.warn(s"FacilityBenefitShieldChargeRequest: ${player.Name} is seated in an imaginary vehicle")
|
log.warn(
|
||||||
}
|
s"FacilityBenefitShieldChargeRequest: ${player.Name} can not find vehicle ${vehicleGuid.get.guid} in zone ${continent.id}"
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
log.warn(s"FacilityBenefitShieldChargeRequest: ${player.Name} is not seated in a vehicle")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleBattleplan(pkt: BattleplanMessage): Unit = {
|
def handleBattleplan(pkt: BattleplanMessage): Unit = {
|
||||||
|
|
@ -2284,7 +2290,7 @@ class SessionData(
|
||||||
* @param tplayer the player to be killed
|
* @param tplayer the player to be killed
|
||||||
*/
|
*/
|
||||||
def suicide(tplayer: Player): Unit = {
|
def suicide(tplayer: Player): Unit = {
|
||||||
tplayer.History(PlayerSuicide(PlayerSource(tplayer)))
|
tplayer.LogActivity(PlayerSuicide(PlayerSource(tplayer)))
|
||||||
tplayer.Actor ! Player.Die()
|
tplayer.Actor ! Player.Die()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,20 +128,17 @@ class SessionLocalHandlers(
|
||||||
llu.Definition.Packet.ConstructorData(llu).get
|
llu.Definition.Packet.ConstructorData(llu).get
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f))
|
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f))
|
||||||
|
|
||||||
case LocalResponse.LluDespawned(llu) =>
|
case LocalResponse.LluDespawned(lluGuid, position) =>
|
||||||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, llu.Position, unk = 20, 0.8000001f))
|
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk = 20, 0.8000001f))
|
||||||
sendResponse(ObjectDeleteMessage(llu.GUID, 0))
|
sendResponse(ObjectDeleteMessage(lluGuid, 0))
|
||||||
// If the player was holding the LLU, remove it from their tracked special item slot
|
// If the player was holding the LLU, remove it from their tracked special item slot
|
||||||
sessionData.specialItemSlotGuid match {
|
sessionData.specialItemSlotGuid match {
|
||||||
case Some(guid) =>
|
case Some(guid) if guid == lluGuid =>
|
||||||
if (guid == llu.GUID) {
|
sessionData.specialItemSlotGuid = None
|
||||||
sessionData.specialItemSlotGuid = None
|
player.Carrying = None
|
||||||
player.Carrying = None
|
case _ => ()
|
||||||
}
|
|
||||||
case _ => ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case LocalResponse.ObjectDelete(object_guid, unk) =>
|
case LocalResponse.ObjectDelete(object_guid, unk) =>
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,11 @@ class SessionVehicleHandlers(
|
||||||
sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat))
|
sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case VehicleResponse.ObjectDelete(itemGuid) =>
|
||||||
|
if (tplayer_guid != guid) {
|
||||||
|
sendResponse(ObjectDeleteMessage(itemGuid, 0))
|
||||||
|
}
|
||||||
|
|
||||||
case VehicleResponse.Ownership(vehicleGuid) =>
|
case VehicleResponse.Ownership(vehicleGuid) =>
|
||||||
if (tplayer_guid == guid) { // Only the player that owns this vehicle needs the ownership packet
|
if (tplayer_guid == guid) { // Only the player that owns this vehicle needs the ownership packet
|
||||||
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
|
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
package net.psforever.actors.session.support
|
package net.psforever.actors.session.support
|
||||||
|
|
||||||
import akka.actor.{ActorContext, typed}
|
import akka.actor.{ActorContext, typed}
|
||||||
|
import net.psforever.objects.avatar.scoring.EquipmentStat
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
|
@ -9,7 +11,7 @@ import scala.concurrent.duration._
|
||||||
//
|
//
|
||||||
import net.psforever.actors.session.{AvatarActor, ChatActor, SessionActor}
|
import net.psforever.actors.session.{AvatarActor, ChatActor, SessionActor}
|
||||||
import net.psforever.login.WorldSession.{CountAmmunition, CountGrenades, FindAmmoBoxThatUses, FindEquipmentStock, FindToolThatUses, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
import net.psforever.login.WorldSession.{CountAmmunition, CountGrenades, FindAmmoBoxThatUses, FindEquipmentStock, FindToolThatUses, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, Projectile, ProjectileQuality, SourceEntry}
|
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
|
||||||
import net.psforever.objects.entity.SimpleWorldEntity
|
import net.psforever.objects.entity.SimpleWorldEntity
|
||||||
import net.psforever.objects.equipment.{ChargeFireModeDefinition, Equipment, EquipmentSize, FireModeSwitch}
|
import net.psforever.objects.equipment.{ChargeFireModeDefinition, Equipment, EquipmentSize, FireModeSwitch}
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
|
import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
|
||||||
|
|
@ -24,7 +26,8 @@ import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
import net.psforever.objects.zones.{Zone, ZoneProjectile}
|
import net.psforever.objects.zones.{Zone, ZoneProjectile}
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.packet.game.{AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, ReloadMessage, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponLazeTargetPositionMessage, _}
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
|
import net.psforever.packet.game._
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
import net.psforever.types.{ExoSuitType, PlanetSideGUID, Vector3}
|
import net.psforever.types.{ExoSuitType, PlanetSideGUID, Vector3}
|
||||||
|
|
@ -40,6 +43,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
var prefire: mutable.Set[PlanetSideGUID] = mutable.Set.empty //if WeaponFireMessage precedes ChangeFireStateMessage_Start
|
var prefire: mutable.Set[PlanetSideGUID] = mutable.Set.empty //if WeaponFireMessage precedes ChangeFireStateMessage_Start
|
||||||
private[support] var shootingStart: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
private[support] var shootingStart: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
||||||
private[support] var shootingStop: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
private[support] var shootingStop: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
||||||
|
private var ongoingShotsFired: Int = 0
|
||||||
private[support] var shotsWhileDead: Int = 0
|
private[support] var shotsWhileDead: Int = 0
|
||||||
private val projectiles: Array[Option[Projectile]] =
|
private val projectiles: Array[Option[Projectile]] =
|
||||||
Array.fill[Option[Projectile]](Projectile.rangeUID - Projectile.baseUID)(None)
|
Array.fill[Option[Projectile]](Projectile.rangeUID - Projectile.baseUID)(None)
|
||||||
|
|
@ -112,6 +116,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
prefire -= item_guid
|
prefire -= item_guid
|
||||||
shooting += item_guid
|
shooting += item_guid
|
||||||
shootingStart += item_guid -> System.currentTimeMillis()
|
shootingStart += item_guid -> System.currentTimeMillis()
|
||||||
|
ongoingShotsFired = 0
|
||||||
//special case - suppress the decimator's alternate fire mode, by projectile
|
//special case - suppress the decimator's alternate fire mode, by projectile
|
||||||
if (tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
|
if (tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
|
||||||
continent.AvatarEvents ! AvatarServiceMessage(
|
continent.AvatarEvents ! AvatarServiceMessage(
|
||||||
|
|
@ -140,6 +145,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
prefire -= item_guid
|
prefire -= item_guid
|
||||||
shooting += item_guid
|
shooting += item_guid
|
||||||
shootingStart += item_guid -> System.currentTimeMillis()
|
shootingStart += item_guid -> System.currentTimeMillis()
|
||||||
|
ongoingShotsFired = 0
|
||||||
continent.AvatarEvents ! AvatarServiceMessage(
|
continent.AvatarEvents ! AvatarServiceMessage(
|
||||||
continent.id,
|
continent.id,
|
||||||
AvatarAction.ChangeFireState_Start(player.GUID, item_guid)
|
AvatarAction.ChangeFireState_Start(player.GUID, item_guid)
|
||||||
|
|
@ -168,8 +174,10 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
continent.id,
|
continent.id,
|
||||||
AvatarAction.ChangeFireState_Start(pguid, item_guid)
|
AvatarAction.ChangeFireState_Start(pguid, item_guid)
|
||||||
)
|
)
|
||||||
|
ongoingShotsFired = ongoingShotsFired + tool.Discharge()
|
||||||
shootingStart += item_guid -> (System.currentTimeMillis() - 1L)
|
shootingStart += item_guid -> (System.currentTimeMillis() - 1L)
|
||||||
}
|
}
|
||||||
|
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(tool.Definition.ObjectId,ongoingShotsFired,0,0))
|
||||||
tool.FireMode match {
|
tool.FireMode match {
|
||||||
case _: ChargeFireModeDefinition =>
|
case _: ChargeFireModeDefinition =>
|
||||||
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
|
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
|
||||||
|
|
@ -410,6 +418,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
) =>
|
) =>
|
||||||
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos) match {
|
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos) match {
|
||||||
case Some(resprojectile) =>
|
case Some(resprojectile) =>
|
||||||
|
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||||
sessionData.handleDealingDamage(target, resprojectile)
|
sessionData.handleDealingDamage(target, resprojectile)
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
|
|
@ -447,8 +456,9 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
|
CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
|
||||||
ResolveProjectileInteraction(projectile, resolution1, target, target.Position) match {
|
ResolveProjectileInteraction(projectile, resolution1, target, target.Position) match {
|
||||||
case Some(_projectile) =>
|
case Some(resprojectile) =>
|
||||||
sessionData.handleDealingDamage(target, _projectile)
|
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||||
|
sessionData.handleDealingDamage(target, resprojectile)
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -459,8 +469,9 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
|
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
|
||||||
ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos) match {
|
ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos) match {
|
||||||
case Some(_projectile) =>
|
case Some(resprojectile) =>
|
||||||
sessionData.handleDealingDamage(target, _projectile)
|
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||||
|
sessionData.handleDealingDamage(target, resprojectile)
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -499,8 +510,9 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
CheckForHitPositionDiscrepancy(projectile_guid, hit_pos, target)
|
CheckForHitPositionDiscrepancy(projectile_guid, hit_pos, target)
|
||||||
ResolveProjectileInteraction(projectile_guid, DamageResolution.Lash, target, hit_pos) match {
|
ResolveProjectileInteraction(projectile_guid, DamageResolution.Lash, target, hit_pos) match {
|
||||||
case Some(projectile) =>
|
case Some(resprojectile) =>
|
||||||
sessionData.handleDealingDamage(target, projectile)
|
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||||
|
sessionData.handleDealingDamage(target, resprojectile)
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -554,17 +566,29 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
val distanceToOwner = Vector3.DistanceSquared(shotOrigin, player.Position)
|
val distanceToOwner = Vector3.DistanceSquared(shotOrigin, player.Position)
|
||||||
if (distanceToOwner <= acceptableDistanceToOwner) {
|
if (distanceToOwner <= acceptableDistanceToOwner) {
|
||||||
val projectile_info = tool.Projectile
|
val projectile_info = tool.Projectile
|
||||||
val projectile =
|
val wguid = weaponGUID.guid
|
||||||
Projectile(
|
val mountedIn = (continent.turretToWeapon
|
||||||
projectile_info,
|
.find { case (guid, _) => guid == wguid } match {
|
||||||
tool.Definition,
|
case Some((_, turretGuid)) => Some((
|
||||||
tool.FireMode,
|
turretGuid,
|
||||||
PlayerSource(player),
|
continent.GUID(turretGuid).collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }
|
||||||
attribution,
|
))
|
||||||
shotOrigin,
|
case _ => None
|
||||||
angle,
|
}) match {
|
||||||
shotVelocity
|
case Some((guid, Some(entity))) => Some((guid, entity))
|
||||||
)
|
case _ => None
|
||||||
|
}
|
||||||
|
val projectile = new Projectile(
|
||||||
|
projectile_info,
|
||||||
|
tool.Definition,
|
||||||
|
tool.FireMode,
|
||||||
|
mountedIn,
|
||||||
|
PlayerSource(player),
|
||||||
|
attribution,
|
||||||
|
shotOrigin,
|
||||||
|
angle,
|
||||||
|
shotVelocity
|
||||||
|
)
|
||||||
val initialQuality = tool.FireMode match {
|
val initialQuality = tool.FireMode match {
|
||||||
case mode: ChargeFireModeDefinition =>
|
case mode: ChargeFireModeDefinition =>
|
||||||
ProjectileQuality.Modified(
|
ProjectileQuality.Modified(
|
||||||
|
|
@ -641,7 +665,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
}
|
}
|
||||||
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
|
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
|
||||||
prefire += weaponGUID
|
prefire += weaponGUID
|
||||||
tool.Discharge()
|
ongoingShotsFired = ongoingShotsFired + tool.Discharge()
|
||||||
}
|
}
|
||||||
(o, Some(tool))
|
(o, Some(tool))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@ import akka.actor.typed.scaladsl.adapter._
|
||||||
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
|
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
|
||||||
|
import net.psforever.objects.vital.{InGameHistory, ReconstructionActivity, SpawningActivity}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
@ -37,11 +41,12 @@ import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||||
import net.psforever.objects.vehicles._
|
import net.psforever.objects.vehicles._
|
||||||
import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
|
import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.packet.game.objectcreate.ObjectClass
|
import net.psforever.packet.game.{AvatarAwardMessage, AvatarSearchCriteriaMessage, AvatarStatisticsMessage, AwardCompletion, BindPlayerMessage, BindStatus, CargoMountPointStatusMessage, ChangeShortcutBankMessage, ChatChannel, CreateShortcutMessage, DroppodFreefallingMessage, LoadMapMessage, ObjectCreateDetailedMessage, ObjectDeleteMessage, PlanetsideStringAttributeMessage, PlayerStateShiftMessage, SetChatFilterMessage, SetCurrentAvatarMessage, ShiftState}
|
||||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
|
|
||||||
import net.psforever.packet.game.{BeginZoningMessage, DroppodLaunchRequestMessage, ReleaseAvatarRequestMessage, SpawnRequestMessage, WarpgateRequest}
|
|
||||||
import net.psforever.packet.game.{AvatarAwardMessage, AvatarSearchCriteriaMessage, AvatarStatisticsMessage, AwardCompletion, BindPlayerMessage, BindStatus, CargoMountPointStatusMessage, ChangeShortcutBankMessage, ChatChannel, CreateShortcutMessage, DroppodFreefallingMessage, LoadMapMessage, ObjectCreateDetailedMessage, ObjectDeleteMessage, PlanetsideStringAttributeMessage, PlayerStateShiftMessage, SetChatFilterMessage, SetCurrentAvatarMessage, ShiftState, Statistics}
|
|
||||||
import net.psforever.packet.game.{AvatarDeadStateMessage, BroadcastWarpgateUpdateMessage, ChatMsg, ContinentalLockUpdateMessage, DeadState, DensityLevelUpdateMessage, DeployRequestMessage, DeployableInfo, DeployableObjectsInfoMessage, DeploymentAction, DisconnectMessage, DroppodError, DroppodLaunchResponseMessage, FriendsResponse, GenericObjectActionMessage, GenericObjectStateMsg, HotSpotUpdateMessage, ObjectAttachMessage, ObjectCreateMessage, PlanetsideAttributeEnum, PlanetsideAttributeMessage, PropertyOverrideMessage, ReplicationStreamMessage, SetEmpireMessage, TimeOfDayMessage, TriggerEffectMessage, ZoneForcedCavernConnectionsMessage, ZoneInfoMessage, ZoneLockInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo}
|
import net.psforever.packet.game.{AvatarDeadStateMessage, BroadcastWarpgateUpdateMessage, ChatMsg, ContinentalLockUpdateMessage, DeadState, DensityLevelUpdateMessage, DeployRequestMessage, DeployableInfo, DeployableObjectsInfoMessage, DeploymentAction, DisconnectMessage, DroppodError, DroppodLaunchResponseMessage, FriendsResponse, GenericObjectActionMessage, GenericObjectStateMsg, HotSpotUpdateMessage, ObjectAttachMessage, ObjectCreateMessage, PlanetsideAttributeEnum, PlanetsideAttributeMessage, PropertyOverrideMessage, ReplicationStreamMessage, SetEmpireMessage, TimeOfDayMessage, TriggerEffectMessage, ZoneForcedCavernConnectionsMessage, ZoneInfoMessage, ZoneLockInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo}
|
||||||
|
import net.psforever.packet.game.{BeginZoningMessage, DroppodLaunchRequestMessage, ReleaseAvatarRequestMessage, SpawnRequestMessage, WarpgateRequest}
|
||||||
|
import net.psforever.packet.game.DeathStatistic
|
||||||
|
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
|
||||||
|
import net.psforever.packet.game.objectcreate.ObjectClass
|
||||||
import net.psforever.packet.{PlanetSideGamePacket, game}
|
import net.psforever.packet.{PlanetSideGamePacket, game}
|
||||||
import net.psforever.persistence.Savedplayer
|
import net.psforever.persistence.Savedplayer
|
||||||
import net.psforever.services.RemoverActor
|
import net.psforever.services.RemoverActor
|
||||||
|
|
@ -828,6 +833,10 @@ class ZoningOperations(
|
||||||
spawn.LoadZonePhysicalSpawnPoint(zoneId, position, Vector3.Zero, 0 seconds, None)
|
spawn.LoadZonePhysicalSpawnPoint(zoneId, position, Vector3.Zero, 0 seconds, None)
|
||||||
case _ => // not seated as the driver, in which case we can't move
|
case _ => // not seated as the driver, in which case we can't move
|
||||||
}
|
}
|
||||||
|
case _ if !player.isAlive =>
|
||||||
|
Player.Respawn(player)
|
||||||
|
player.Health = 1
|
||||||
|
spawn.LoadZonePhysicalSpawnPoint(continent.id, position, Vector3.z(player.Orientation.z), 0.seconds, None)
|
||||||
case None =>
|
case None =>
|
||||||
spawn.deadState = DeadState.Release // cancel movement updates
|
spawn.deadState = DeadState.Release // cancel movement updates
|
||||||
player.Position = position
|
player.Position = position
|
||||||
|
|
@ -1162,7 +1171,7 @@ class ZoningOperations(
|
||||||
*/
|
*/
|
||||||
def LoadZoneAsPlayer(targetPlayer: Player, zoneId: String): Unit = {
|
def LoadZoneAsPlayer(targetPlayer: Player, zoneId: String): Unit = {
|
||||||
log.debug(s"LoadZoneAsPlayer: ${targetPlayer.avatar.name} loading into $zoneId")
|
log.debug(s"LoadZoneAsPlayer: ${targetPlayer.avatar.name} loading into $zoneId")
|
||||||
if (!zoneReload && zoneId == continent.id) {
|
if (!zoneReload && zoneId.equals(continent.id)) {
|
||||||
if (player.isBackpack) { // important! test the actor-wide player ref, not the parameter
|
if (player.isBackpack) { // important! test the actor-wide player ref, not the parameter
|
||||||
// respawning from unregistered player
|
// respawning from unregistered player
|
||||||
TaskWorkflow.execute(sessionData.registerAvatar(targetPlayer))
|
TaskWorkflow.execute(sessionData.registerAvatar(targetPlayer))
|
||||||
|
|
@ -1436,6 +1445,10 @@ class ZoningOperations(
|
||||||
Deployables.Disown(continent, avatar, context.self)
|
Deployables.Disown(continent, avatar, context.self)
|
||||||
spawn.drawDeloyableIcon = spawn.RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone
|
spawn.drawDeloyableIcon = spawn.RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone
|
||||||
sessionData.squad.squadSetup = sessionData.squad.ZoneChangeSquadSetup
|
sessionData.squad.squadSetup = sessionData.squad.ZoneChangeSquadSetup
|
||||||
|
val lastSeen = sessionData.avatarResponse.lastSeenStreamMessage
|
||||||
|
lastSeen.indices.foreach { index =>
|
||||||
|
lastSeen(index) = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2542,17 +2555,26 @@ class ZoningOperations(
|
||||||
nextSpawnPoint = physSpawnPoint
|
nextSpawnPoint = physSpawnPoint
|
||||||
shiftPosition = Some(pos)
|
shiftPosition = Some(pos)
|
||||||
shiftOrientation = Some(ori)
|
shiftOrientation = Some(ori)
|
||||||
|
val toZoneNumber = if (continent.id.equals(zoneId)) {
|
||||||
|
continent.Number
|
||||||
|
} else {
|
||||||
|
Zones.zones.find { _.id.equals(zoneId) }.orElse(Some(Zone.Nowhere)).get.Number
|
||||||
|
}
|
||||||
|
val toSpawnPoint = physSpawnPoint.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }
|
||||||
respawnTimer = context.system.scheduler.scheduleOnce(respawnTime) {
|
respawnTimer = context.system.scheduler.scheduleOnce(respawnTime) {
|
||||||
if (player.isBackpack) { // if the player is dead, he is handled as dead infantry, even if he died in a vehicle
|
if (player.isBackpack) { // if the player is dead, he is handled as dead infantry, even if he died in a vehicle
|
||||||
// new player is spawning
|
// new player is spawning
|
||||||
val newPlayer = RespawnClone(player)
|
val newPlayer = RespawnClone(player)
|
||||||
newPlayer.Position = pos
|
newPlayer.Position = pos
|
||||||
newPlayer.Orientation = ori
|
newPlayer.Orientation = ori
|
||||||
|
newPlayer.LogActivity(SpawningActivity(PlayerSource(newPlayer), toZoneNumber, toSpawnPoint))
|
||||||
LoadZoneAsPlayer(newPlayer, zoneId)
|
LoadZoneAsPlayer(newPlayer, zoneId)
|
||||||
} else {
|
} else {
|
||||||
avatarActor ! AvatarActor.DeactivateActiveImplants()
|
avatarActor ! AvatarActor.DeactivateActiveImplants()
|
||||||
interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||||
case Some(vehicle: Vehicle) => // driver or passenger in vehicle using a warp gate, or a droppod
|
case Some(vehicle: Vehicle) => // driver or passenger in vehicle using a warp gate, or a droppod
|
||||||
|
InGameHistory.SpawnReconstructionActivity(vehicle, toZoneNumber, toSpawnPoint)
|
||||||
|
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, toSpawnPoint)
|
||||||
LoadZoneInVehicle(vehicle, pos, ori, zoneId)
|
LoadZoneInVehicle(vehicle, pos, ori, zoneId)
|
||||||
|
|
||||||
case _ if player.HasGUID => // player is deconstructing self or instant action
|
case _ if player.HasGUID => // player is deconstructing self or instant action
|
||||||
|
|
@ -2564,11 +2586,13 @@ class ZoningOperations(
|
||||||
)
|
)
|
||||||
player.Position = pos
|
player.Position = pos
|
||||||
player.Orientation = ori
|
player.Orientation = ori
|
||||||
|
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, toSpawnPoint)
|
||||||
LoadZoneAsPlayer(player, zoneId)
|
LoadZoneAsPlayer(player, zoneId)
|
||||||
|
|
||||||
case _ => //player is logging in
|
case _ => //player is logging in
|
||||||
player.Position = pos
|
player.Position = pos
|
||||||
player.Orientation = ori
|
player.Orientation = ori
|
||||||
|
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, toSpawnPoint)
|
||||||
LoadZoneAsPlayer(player, zoneId)
|
LoadZoneAsPlayer(player, zoneId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2698,7 +2722,6 @@ class ZoningOperations(
|
||||||
}
|
}
|
||||||
|
|
||||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
|
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
|
||||||
//TODO if Medkit does not have shortcut, add to a free slot or write over slot 64
|
|
||||||
avatar.shortcuts
|
avatar.shortcuts
|
||||||
.zipWithIndex
|
.zipWithIndex
|
||||||
.collect { case (Some(shortcut), index) =>
|
.collect { case (Some(shortcut), index) =>
|
||||||
|
|
@ -2739,7 +2762,7 @@ class ZoningOperations(
|
||||||
}
|
}
|
||||||
(0 to 30).foreach(_ => {
|
(0 to 30).foreach(_ => {
|
||||||
//TODO 30 for a new character only?
|
//TODO 30 for a new character only?
|
||||||
sendResponse(AvatarStatisticsMessage(2, Statistics(0L)))
|
sendResponse(AvatarStatisticsMessage(DeathStatistic(0L)))
|
||||||
})
|
})
|
||||||
if (tplayer.ExoSuit == ExoSuitType.MAX) {
|
if (tplayer.ExoSuit == ExoSuitType.MAX) {
|
||||||
sendResponse(PlanetsideAttributeMessage(guid, 7, tplayer.Capacitor.toLong))
|
sendResponse(PlanetsideAttributeMessage(guid, 7, tplayer.Capacitor.toLong))
|
||||||
|
|
@ -2787,13 +2810,13 @@ class ZoningOperations(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case (Some(vehicle), Some(0)) =>
|
case (Some(vehicle), Some(0)) =>
|
||||||
//driver; summon any passengers and cargo vehicles left behind on previous continent
|
//driver of vehicle
|
||||||
if (vehicle.Jammed) {
|
if (vehicle.Jammed) {
|
||||||
//TODO something better than just canceling?
|
//TODO something better than just canceling?
|
||||||
vehicle.Actor ! JammableUnit.ClearJammeredStatus()
|
vehicle.Actor ! JammableUnit.ClearJammeredStatus()
|
||||||
vehicle.Actor ! JammableUnit.ClearJammeredSound()
|
vehicle.Actor ! JammableUnit.ClearJammeredSound()
|
||||||
}
|
}
|
||||||
//positive shield strength
|
// positive shield strength
|
||||||
if (vehicle.Definition.MaxShields > 0) {
|
if (vehicle.Definition.MaxShields > 0) {
|
||||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, vehicle.Definition.shieldUiAttribute, vehicle.Shields))
|
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, vehicle.Definition.shieldUiAttribute, vehicle.Shields))
|
||||||
}
|
}
|
||||||
|
|
@ -2805,6 +2828,11 @@ class ZoningOperations(
|
||||||
if (vehicle.Definition.MaxCapacitor > 0) {
|
if (vehicle.Definition.MaxCapacitor > 0) {
|
||||||
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 113, vehicle.Capacitor))
|
sendResponse(PlanetsideAttributeMessage(vehicle.GUID, 113, vehicle.Capacitor))
|
||||||
}
|
}
|
||||||
|
// vehicle entering zone
|
||||||
|
if (vehicle.History.headOption.exists { _.isInstanceOf[SpawningActivity] }) {
|
||||||
|
vehicle.LogActivity(ReconstructionActivity(VehicleSource(vehicle), continent.Number, None))
|
||||||
|
}
|
||||||
|
// summon any passengers and cargo vehicles left behind on previous continent
|
||||||
LoadZoneTransferPassengerMessages(
|
LoadZoneTransferPassengerMessages(
|
||||||
guid,
|
guid,
|
||||||
continent.id,
|
continent.id,
|
||||||
|
|
@ -2825,12 +2853,31 @@ class ZoningOperations(
|
||||||
} else if (originalDeadState == DeadState.Dead || player.Health == 0) {
|
} else if (originalDeadState == DeadState.Dead || player.Health == 0) {
|
||||||
//killed during spawn setup or possibly a relog into a corpse (by accident?)
|
//killed during spawn setup or possibly a relog into a corpse (by accident?)
|
||||||
player.Actor ! Player.Die()
|
player.Actor ! Player.Die()
|
||||||
|
} else {
|
||||||
|
AvatarActor.savePlayerData(player)
|
||||||
|
sessionData.displayCharSavedMsgThenRenewTimer(
|
||||||
|
Config.app.game.savedMsg.short.fixed,
|
||||||
|
Config.app.game.savedMsg.short.variable
|
||||||
|
)
|
||||||
|
//player
|
||||||
|
val effortBy = nextSpawnPoint
|
||||||
|
.collect { case sp: SpawnTube => (sp, continent.GUID(sp.Owner.GUID)) }
|
||||||
|
.collect {
|
||||||
|
case (_, Some(v: Vehicle)) => continent.GUID(v.Owner)
|
||||||
|
case (sp, Some(_: Building)) => Some(sp)
|
||||||
|
}
|
||||||
|
.collect { case Some(thing: PlanetSideGameObject with FactionAffinity) => Some(SourceEntry(thing)) }
|
||||||
|
.flatten
|
||||||
|
player.LogActivity({
|
||||||
|
if (player.History.headOption.exists { _.isInstanceOf[SpawningActivity] }) {
|
||||||
|
ReconstructionActivity(PlayerSource(player), continent.Number, effortBy)
|
||||||
|
} else {
|
||||||
|
SpawningActivity(PlayerSource(player), continent.Number, effortBy)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//ride
|
||||||
|
|
||||||
}
|
}
|
||||||
AvatarActor.savePlayerData(player)
|
|
||||||
sessionData.displayCharSavedMsgThenRenewTimer(
|
|
||||||
Config.app.game.savedMsg.short.fixed,
|
|
||||||
Config.app.game.savedMsg.short.variable
|
|
||||||
)
|
|
||||||
upstreamMessageCount = 0
|
upstreamMessageCount = 0
|
||||||
setAvatar = true
|
setAvatar = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,10 @@ class BuildingActor(
|
||||||
|
|
||||||
def setup(details: BuildingControlDetails): Behavior[Command] = {
|
def setup(details: BuildingControlDetails): Behavior[Command] = {
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
|
case ReceptionistListing(InterstellarClusterService.InterstellarClusterServiceKey.Listing(listings))
|
||||||
|
if listings.isEmpty =>
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case ReceptionistListing(InterstellarClusterService.InterstellarClusterServiceKey.Listing(listings)) =>
|
case ReceptionistListing(InterstellarClusterService.InterstellarClusterServiceKey.Listing(listings)) =>
|
||||||
switchToBehavior(details.copy(interstellarCluster = listings.head))
|
switchToBehavior(details.copy(interstellarCluster = listings.head))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package net.psforever.actors.zone
|
||||||
|
|
||||||
import akka.actor.typed.{ActorRef, Behavior, SupervisorStrategy}
|
import akka.actor.typed.{ActorRef, Behavior, SupervisorStrategy}
|
||||||
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.ce.Deployable
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.serverobject.structures.{StructureType, WarpGate}
|
import net.psforever.objects.serverobject.structures.{StructureType, WarpGate}
|
||||||
|
|
@ -11,12 +10,13 @@ import net.psforever.objects.zones.blockmap.{BlockMapEntity, SectorGroup}
|
||||||
import net.psforever.objects.{ConstructionItem, Player, Vehicle}
|
import net.psforever.objects.{ConstructionItem, Player, Vehicle}
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
import scala.collection.mutable.ListBuffer
|
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
import net.psforever.actors.zone.building.MajorFacilityLogic
|
import net.psforever.actors.zone.building.MajorFacilityLogic
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.util.Database._
|
import net.psforever.util.Database._
|
||||||
import net.psforever.persistence
|
import net.psforever.persistence
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ object ZoneActor {
|
||||||
.supervise[Command] {
|
.supervise[Command] {
|
||||||
Behaviors.setup(context => new ZoneActor(context, zone))
|
Behaviors.setup(context => new ZoneActor(context, zone))
|
||||||
}
|
}
|
||||||
.onFailure[Exception](SupervisorStrategy.restart)
|
.onFailure[Exception](SupervisorStrategy.resume)
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
|
|
||||||
|
|
@ -73,13 +73,13 @@ object ZoneActor {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
||||||
extends AbstractBehavior[ZoneActor.Command](context) {
|
extends AbstractBehavior[ZoneActor.Command](context) {
|
||||||
|
|
||||||
import ZoneActor._
|
import ZoneActor._
|
||||||
import ctx._
|
import ctx._
|
||||||
|
|
||||||
private[this] val log = org.log4s.getLogger
|
private[this] val log = org.log4s.getLogger
|
||||||
val players: ListBuffer[Player] = ListBuffer()
|
val players: mutable.ListBuffer[Player] = mutable.ListBuffer()
|
||||||
|
|
||||||
zone.actor = context.self
|
zone.actor = context.self
|
||||||
zone.init(context.toClassic)
|
zone.init(context.toClassic)
|
||||||
|
|
@ -102,7 +102,7 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
||||||
case Failure(e) => log.error(e.getMessage)
|
case Failure(e) => log.error(e.getMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onMessage(msg: Command): Behavior[Command] = {
|
def onMessage(msg: Command): Behavior[Command] = {
|
||||||
msg match {
|
msg match {
|
||||||
case GetZone(replyTo) =>
|
case GetZone(replyTo) =>
|
||||||
replyTo ! ZoneResponse(zone)
|
replyTo ! ZoneResponse(zone)
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,21 @@ case object CavernFacilityLogic
|
||||||
def amenityStateChange(details: BuildingWrapper, entity: Amenity, data: Option[Any]): Behavior[Command] = {
|
def amenityStateChange(details: BuildingWrapper, entity: Amenity, data: Option[Any]): Behavior[Command] = {
|
||||||
entity match {
|
entity match {
|
||||||
case terminal: CaptureTerminal =>
|
case terminal: CaptureTerminal =>
|
||||||
|
val building = details.building
|
||||||
// Notify amenities that listen for CC hack state changes, e.g. wall turrets to dismount seated players
|
// Notify amenities that listen for CC hack state changes, e.g. wall turrets to dismount seated players
|
||||||
details.building.Amenities.filter(x => x.isInstanceOf[CaptureTerminalAware]).foreach(amenity => {
|
data match {
|
||||||
data match {
|
case Some(isResecured: Boolean) =>
|
||||||
case Some(isResecured: Boolean) => amenity.Actor ! CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured)
|
//pass hack information to amenities
|
||||||
case _ => log(details).warn("CaptureTerminal AmenityStateChange was received with no attached data.")
|
building.Amenities.filter(x => x.isInstanceOf[CaptureTerminalAware]).foreach(amenity => {
|
||||||
}
|
amenity.Actor ! CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured)
|
||||||
})
|
})
|
||||||
|
case _ =>
|
||||||
|
log(details).warn("CaptureTerminal AmenityStateChange was received with no attached data.")
|
||||||
|
}
|
||||||
// When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state
|
// When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state
|
||||||
details.building.HackableAmenities.foreach(amenity => {
|
building.HackableAmenities.foreach(amenity => {
|
||||||
if (amenity.HackedBy.isDefined) {
|
if (amenity.HackedBy.isDefined) {
|
||||||
details.building.Zone.LocalEvents ! LocalServiceMessage(amenity.Zone.id,LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity))
|
building.Zone.LocalEvents ! LocalServiceMessage(amenity.Zone.id,LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// No map update needed - will be sent by `HackCaptureActor` when required
|
// No map update needed - will be sent by `HackCaptureActor` when required
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,21 @@ case object FacilityLogic
|
||||||
def amenityStateChange(details: BuildingWrapper, entity: Amenity, data: Option[Any]): Behavior[Command] = {
|
def amenityStateChange(details: BuildingWrapper, entity: Amenity, data: Option[Any]): Behavior[Command] = {
|
||||||
entity match {
|
entity match {
|
||||||
case terminal: CaptureTerminal =>
|
case terminal: CaptureTerminal =>
|
||||||
|
val building = details.building
|
||||||
// Notify amenities that listen for CC hack state changes, e.g. wall turrets to dismount seated players
|
// Notify amenities that listen for CC hack state changes, e.g. wall turrets to dismount seated players
|
||||||
details.building.Amenities.filter(x => x.isInstanceOf[CaptureTerminalAware]).foreach(amenity => {
|
data match {
|
||||||
data match {
|
case Some(isResecured: Boolean) =>
|
||||||
case Some(isResecured: Boolean) => amenity.Actor ! CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured)
|
//pass hack information to amenities
|
||||||
case _ => log(details).warn("CaptureTerminal AmenityStateChange was received with no attached data.")
|
building.Amenities.filter(x => x.isInstanceOf[CaptureTerminalAware]).foreach(amenity => {
|
||||||
}
|
amenity.Actor ! CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured)
|
||||||
})
|
})
|
||||||
|
case _ =>
|
||||||
|
log(details).warn("CaptureTerminal AmenityStateChange was received with no attached data.")
|
||||||
|
}
|
||||||
// When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state
|
// When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state
|
||||||
details.building.HackableAmenities.foreach(amenity => {
|
building.HackableAmenities.foreach(amenity => {
|
||||||
if (amenity.HackedBy.isDefined) {
|
if (amenity.HackedBy.isDefined) {
|
||||||
details.building.Zone.LocalEvents ! LocalServiceMessage(amenity.Zone.id,LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity))
|
building.Zone.LocalEvents ! LocalServiceMessage(amenity.Zone.id, LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// No map update needed - will be sent by `HackCaptureActor` when required
|
// No map update needed - will be sent by `HackCaptureActor` when required
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import net.psforever.actors.zone.{BuildingActor, BuildingControlDetails, ZoneAct
|
||||||
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
|
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||||
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior}
|
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior}
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.services.{InterstellarClusterService, Service}
|
import net.psforever.services.{InterstellarClusterService, Service}
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
|
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
|
||||||
|
|
@ -164,17 +165,21 @@ case object MajorFacilityLogic
|
||||||
details.building.Zone.actor ! ZoneActor.ZoneMapUpdate()
|
details.building.Zone.actor ! ZoneActor.ZoneMapUpdate()
|
||||||
}
|
}
|
||||||
case terminal: CaptureTerminal =>
|
case terminal: CaptureTerminal =>
|
||||||
|
val building = details.building
|
||||||
// Notify amenities that listen for CC hack state changes, e.g. wall turrets to dismount seated players
|
// Notify amenities that listen for CC hack state changes, e.g. wall turrets to dismount seated players
|
||||||
details.building.Amenities.filter(x => x.isInstanceOf[CaptureTerminalAware]).foreach(amenity => {
|
data match {
|
||||||
data match {
|
case Some(isResecured: Boolean) =>
|
||||||
case Some(isResecured: Boolean) => amenity.Actor ! CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured)
|
//pass hack information to amenities
|
||||||
case _ => log(details).warn("CaptureTerminal AmenityStateChange was received with no attached data.")
|
building.Amenities.filter(x => x.isInstanceOf[CaptureTerminalAware]).foreach(amenity => {
|
||||||
}
|
amenity.Actor ! CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured)
|
||||||
})
|
})
|
||||||
|
case _ =>
|
||||||
|
log(details).warn("CaptureTerminal AmenityStateChange was received with no attached data.")
|
||||||
|
}
|
||||||
// When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state
|
// When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state
|
||||||
details.building.HackableAmenities.foreach(amenity => {
|
building.HackableAmenities.foreach(amenity => {
|
||||||
if (amenity.HackedBy.isDefined) {
|
if (amenity.HackedBy.isDefined) {
|
||||||
details.building.Zone.LocalEvents ! LocalServiceMessage(amenity.Zone.id,LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity))
|
building.Zone.LocalEvents ! LocalServiceMessage(amenity.Zone.id,LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// No map update needed - will be sent by `HackCaptureActor` when required
|
// No map update needed - will be sent by `HackCaptureActor` when required
|
||||||
|
|
@ -330,6 +335,14 @@ case object MajorFacilityLogic
|
||||||
|
|
||||||
def alertToFactionChange(details: BuildingWrapper, building: Building): Behavior[Command] = {
|
def alertToFactionChange(details: BuildingWrapper, building: Building): Behavior[Command] = {
|
||||||
alignForceDomeStatus(details)
|
alignForceDomeStatus(details)
|
||||||
|
val bldg = details.building
|
||||||
|
//the presence of the flag means that we are involved in an ongoing llu hack
|
||||||
|
(bldg.GetFlag, bldg.CaptureTerminal) match {
|
||||||
|
case (Some(flag), Some(terminal)) if (flag.Target eq building) && flag.Faction != building.Faction =>
|
||||||
|
//our hack destination may have been compromised and the hack needs to be cancelled
|
||||||
|
bldg.Zone.LocalEvents ! LocalServiceMessage("", LocalAction.ResecureCaptureTerminal(terminal, PlayerSource.Nobody))
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ import net.psforever.objects.guid._
|
||||||
import net.psforever.objects.inventory.{Container, InventoryItem}
|
import net.psforever.objects.inventory.{Container, InventoryItem}
|
||||||
import net.psforever.objects.locker.LockerContainer
|
import net.psforever.objects.locker.LockerContainer
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.containable.Containable
|
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
||||||
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
|
import net.psforever.objects.sourcing.AmenitySource
|
||||||
|
import net.psforever.objects.vital.TerminalUsedActivity
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.types.{ExoSuitType, PlanetSideGUID, TransactionType, Vector3}
|
import net.psforever.types.{ExoSuitType, PlanetSideGUID, TransactionType, Vector3}
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
|
|
@ -31,7 +34,8 @@ object WorldSession {
|
||||||
* @return 1 for `true`; 0 for `false`
|
* @return 1 for `true`; 0 for `false`
|
||||||
*/
|
*/
|
||||||
implicit def boolToInt(b: Boolean): Int = if (b) 1 else 0
|
implicit def boolToInt(b: Boolean): Int = if (b) 1 else 0
|
||||||
private implicit val timeout = new Timeout(5000 milliseconds)
|
|
||||||
|
private implicit val timeout: Timeout = new Timeout(5000 milliseconds)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this for placing equipment that has already been registered into a container,
|
* Use this for placing equipment that has already been registered into a container,
|
||||||
|
|
@ -185,7 +189,6 @@ object WorldSession {
|
||||||
val localZone = obj.Zone
|
val localZone = obj.Zone
|
||||||
TaskBundle(
|
TaskBundle(
|
||||||
new StraightforwardTask() {
|
new StraightforwardTask() {
|
||||||
private val localContainer = obj
|
|
||||||
private val localItem = item
|
private val localItem = item
|
||||||
private val localSlot = slot
|
private val localSlot = slot
|
||||||
private val localFunc: (Equipment, Int) => Future[Any] = PutEquipmentInInventorySlot(obj)
|
private val localFunc: (Equipment, Int) => Future[Any] = PutEquipmentInInventorySlot(obj)
|
||||||
|
|
@ -556,10 +559,12 @@ object WorldSession {
|
||||||
val tile = item.Definition.Tile
|
val tile = item.Definition.Tile
|
||||||
destination.Inventory.CheckCollisionsVar(dest, tile.Width, tile.Height)
|
destination.Inventory.CheckCollisionsVar(dest, tile.Width, tile.Height)
|
||||||
} match {
|
} match {
|
||||||
case Success(Nil) =>
|
case Success(Nil)
|
||||||
|
if ContainableBehavior.PermitEquipmentStow(destination, item, dest) =>
|
||||||
//no swap item
|
//no swap item
|
||||||
(true, None)
|
(true, None)
|
||||||
case Success(List(swapEntry: InventoryItem)) =>
|
case Success(List(swapEntry: InventoryItem))
|
||||||
|
if ContainableBehavior.PermitEquipmentStow(destination, item, dest) =>
|
||||||
//the swap item is to be registered to the source's zone
|
//the swap item is to be registered to the source's zone
|
||||||
(true, Some(swapEntry.obj.GUID))
|
(true, Some(swapEntry.obj.GUID))
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -568,13 +573,13 @@ object WorldSession {
|
||||||
}
|
}
|
||||||
if (performSwap) {
|
if (performSwap) {
|
||||||
def moveItemTaskFunc(toSlot: Int): Task = new StraightforwardTask() {
|
def moveItemTaskFunc(toSlot: Int): Task = new StraightforwardTask() {
|
||||||
val localGUID = swapItemGUID //the swap item's original GUID, if any swap item
|
val localGUID: Option[PlanetSideGUID] = swapItemGUID //the swap item's original GUID, if any swap item
|
||||||
val localChannel = toChannel
|
val localChannel: String = toChannel
|
||||||
val localSource = source
|
val localSource: PlanetSideServerObject with Container = source
|
||||||
val localDestination = destination
|
val localDestination: PlanetSideServerObject with Container = destination
|
||||||
val localItem = item
|
val localItem: Equipment = item
|
||||||
val localDestSlot = dest
|
val localDestSlot: Int = dest
|
||||||
val localSrcSlot = toSlot
|
val localSrcSlot: Int = toSlot
|
||||||
val localMoveOnComplete: Try[Any] => Unit = {
|
val localMoveOnComplete: Try[Any] => Unit = {
|
||||||
case Success(Containable.ItemPutInSlot(_, _, _, Some(swapItem))) =>
|
case Success(Containable.ItemPutInSlot(_, _, _, Some(swapItem))) =>
|
||||||
//swapItem is not registered right now, we can not drop the item without re-registering it
|
//swapItem is not registered right now, we can not drop the item without re-registering it
|
||||||
|
|
@ -673,13 +678,13 @@ object WorldSession {
|
||||||
}
|
}
|
||||||
if (performSwap) {
|
if (performSwap) {
|
||||||
def moveItemTaskFunc(toSlot: Int): Task = new StraightforwardTask() {
|
def moveItemTaskFunc(toSlot: Int): Task = new StraightforwardTask() {
|
||||||
val localGUID = swapItemGUID //the swap item's original GUID, if any swap item
|
val localGUID: Option[PlanetSideGUID] = swapItemGUID //the swap item's original GUID, if any swap item
|
||||||
val localChannel = toChannel
|
val localChannel: String = toChannel
|
||||||
val localSource = source
|
val localSource: PlanetSideServerObject with Container = source
|
||||||
val localDestination = destination
|
val localDestination: PlanetSideServerObject with Container = destination
|
||||||
val localItem = item
|
val localItem: Equipment = item
|
||||||
val localDestSlot = dest
|
val localDestSlot: Int = dest
|
||||||
val localSrcSlot = toSlot
|
val localSrcSlot: Int = toSlot
|
||||||
val localMoveOnComplete: Try[Any] => Unit = {
|
val localMoveOnComplete: Try[Any] => Unit = {
|
||||||
case Success(Containable.ItemPutInSlot(_, _, _, Some(swapItem))) =>
|
case Success(Containable.ItemPutInSlot(_, _, _, Some(swapItem))) =>
|
||||||
//swapItem is not registered right now, we can not drop the item without re-registering it
|
//swapItem is not registered right now, we can not drop the item without re-registering it
|
||||||
|
|
@ -936,6 +941,11 @@ object WorldSession {
|
||||||
def TerminalResult(guid: PlanetSideGUID, player: Player, transaction: TransactionType.Value)(
|
def TerminalResult(guid: PlanetSideGUID, player: Player, transaction: TransactionType.Value)(
|
||||||
result: Boolean
|
result: Boolean
|
||||||
): Unit = {
|
): Unit = {
|
||||||
|
if (result) {
|
||||||
|
player.Zone.GUID(guid).collect {
|
||||||
|
case term: Terminal => player.LogActivity(TerminalUsedActivity(AmenitySource(term), transaction))
|
||||||
|
}
|
||||||
|
}
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
player.Name,
|
player.Name,
|
||||||
AvatarAction.TerminalOrderResult(guid, transaction, result)
|
AvatarAction.TerminalOrderResult(guid, transaction, result)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import akka.actor.{ActorContext, Props}
|
import akka.actor.{ActorContext, Props}
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, SourceEntry}
|
|
||||||
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
import net.psforever.objects.ce.{Deployable, DeployedItem}
|
||||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
import net.psforever.objects.vital.etc.TriggerUsedReason
|
import net.psforever.objects.vital.etc.TriggerUsedReason
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
|
|
@ -35,7 +35,7 @@ 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) = {
|
override def Initialize(obj: Deployable, context: ActorContext): Unit = {
|
||||||
obj.Actor =
|
obj.Actor =
|
||||||
context.actorOf(Props(classOf[BoomerDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
|
context.actorOf(Props(classOf[BoomerDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ class BoomerDeployableControl(mine: BoomerDeployable)
|
||||||
}
|
}
|
||||||
zone.AvatarEvents! AvatarServiceMessage(
|
zone.AvatarEvents! AvatarServiceMessage(
|
||||||
zone.id,
|
zone.id,
|
||||||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, trigger.GUID)
|
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid)
|
||||||
)
|
)
|
||||||
TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, trigger))
|
TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, trigger))
|
||||||
case None => ;
|
case None => ;
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ private class DefinitionWrappedInVitality(definition: ObjectDefinition)
|
||||||
case p: ProjectileDefinition => p
|
case p: ProjectileDefinition => p
|
||||||
case _ => GlobalDefinitions.no_projectile
|
case _ => GlobalDefinitions.no_projectile
|
||||||
}
|
}
|
||||||
|
Name = { definition.Name }
|
||||||
DefaultHealth = 1 //just cuz
|
DefaultHealth = 1 //just cuz
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorContext, ActorRef, Props}
|
import akka.actor.{Actor, ActorContext, ActorRef, Props}
|
||||||
import net.psforever.objects.ballistics.{DeployableSource, PlayerSource, SourceEntry}
|
|
||||||
import net.psforever.objects.ce._
|
import net.psforever.objects.ce._
|
||||||
import net.psforever.objects.definition.{DeployableDefinition, ExoSuitDefinition}
|
import net.psforever.objects.definition.{DeployableDefinition, ExoSuitDefinition}
|
||||||
import net.psforever.objects.definition.converter.SmallDeployableConverter
|
import net.psforever.objects.definition.converter.SmallDeployableConverter
|
||||||
|
|
@ -12,13 +11,14 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
|
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
|
||||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||||
|
import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, SourceEntry, UniquePlayer}
|
||||||
import net.psforever.objects.vital.etc.TrippedMineReason
|
import net.psforever.objects.vital.etc.TrippedMineReason
|
||||||
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
|
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
|
||||||
import net.psforever.objects.vital.{SimpleResolutions, Vitality}
|
import net.psforever.objects.vital.{SimpleResolutions, Vitality}
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.types.{ExoSuitType, Vector3}
|
import net.psforever.types.{CharacterSex, ExoSuitType, Vector3}
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||||
|
|
@ -127,7 +127,7 @@ object ExplosiveDeployableControl {
|
||||||
* @param damage na
|
* @param damage na
|
||||||
*/
|
*/
|
||||||
def DamageResolution(target: ExplosiveDeployable, cause: DamageResult, damage: Int): Unit = {
|
def DamageResolution(target: ExplosiveDeployable, cause: DamageResult, damage: Int): Unit = {
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
if (cause.interaction.cause.source.SympatheticExplosion) {
|
if (cause.interaction.cause.source.SympatheticExplosion) {
|
||||||
explodes(target, cause)
|
explodes(target, cause)
|
||||||
DestructionAwareness(target, cause)
|
DestructionAwareness(target, cause)
|
||||||
|
|
@ -324,32 +324,32 @@ object MineDeployableControl {
|
||||||
val deployableSource = DeployableSource(mine)
|
val deployableSource = DeployableSource(mine)
|
||||||
val blame = mine.OwnerName match {
|
val blame = mine.OwnerName match {
|
||||||
case Some(name) =>
|
case Some(name) =>
|
||||||
val(charId, exosuit, seated): (Long, ExoSuitType.Value, Boolean) = mine.Zone
|
val(charId, exosuit, seatedIn): (Long, ExoSuitType.Value, Option[(SourceEntry, Int)]) = mine.Zone
|
||||||
.LivePlayers
|
.LivePlayers
|
||||||
.find { _.Name.equals(name) } match {
|
.find { _.Name.equals(name) } match {
|
||||||
case Some(player) =>
|
case Some(player) =>
|
||||||
//if the owner is alive in the same zone as the mine, use data from their body to create the source
|
//if the owner is alive in the same zone as the mine, use data from their body to create the source
|
||||||
(player.CharId, player.ExoSuit, player.VehicleSeated.nonEmpty)
|
(player.CharId, player.ExoSuit, PlayerSource.mountableAndSeat(player))
|
||||||
case None =>
|
case None =>
|
||||||
//if the owner is as dead as a corpse or is not in the same zone as the mine, use defaults
|
//if the owner is as dead as a corpse or is not in the same zone as the mine, use defaults
|
||||||
(0L, ExoSuitType.Standard, false)
|
(0L, ExoSuitType.Standard, None)
|
||||||
}
|
}
|
||||||
val faction = mine.Faction
|
val faction = mine.Faction
|
||||||
PlayerSource(
|
PlayerSource(
|
||||||
name,
|
|
||||||
charId,
|
|
||||||
GlobalDefinitions.avatar,
|
GlobalDefinitions.avatar,
|
||||||
mine.Faction,
|
|
||||||
exosuit,
|
exosuit,
|
||||||
seated,
|
seatedIn,
|
||||||
100,
|
health = 100,
|
||||||
100,
|
armor = 0,
|
||||||
mine.Position,
|
mine.Position,
|
||||||
Vector3.Zero,
|
Vector3.Zero,
|
||||||
None,
|
None,
|
||||||
crouching = false,
|
crouching = false,
|
||||||
jumping = false,
|
jumping = false,
|
||||||
ExoSuitDefinition.Select(exosuit, faction)
|
ExoSuitDefinition.Select(exosuit, faction),
|
||||||
|
bep = 0,
|
||||||
|
kills = Nil,
|
||||||
|
UniquePlayer(charId, name, CharacterSex.Male, mine.Faction)
|
||||||
)
|
)
|
||||||
case None =>
|
case None =>
|
||||||
//credit where credit is due
|
//credit where credit is due
|
||||||
|
|
|
||||||
|
|
@ -5647,10 +5647,10 @@ object GlobalDefinitions {
|
||||||
advanced_ace.Name = "advanced_ace"
|
advanced_ace.Name = "advanced_ace"
|
||||||
advanced_ace.Size = EquipmentSize.Rifle
|
advanced_ace.Size = EquipmentSize.Rifle
|
||||||
advanced_ace.Modes += new ConstructionFireMode {
|
advanced_ace.Modes += new ConstructionFireMode {
|
||||||
Item(DeployedItem.portable_manned_turret, Set(Certification.AssaultEngineering))
|
Item(DeployedItem.tank_traps, Set(Certification.FortificationEngineering))
|
||||||
}
|
}
|
||||||
advanced_ace.Modes += new ConstructionFireMode {
|
advanced_ace.Modes += new ConstructionFireMode {
|
||||||
Item(DeployedItem.tank_traps, Set(Certification.FortificationEngineering))
|
Item(DeployedItem.portable_manned_turret, Set(Certification.AssaultEngineering))
|
||||||
}
|
}
|
||||||
advanced_ace.Modes += new ConstructionFireMode {
|
advanced_ace.Modes += new ConstructionFireMode {
|
||||||
Item(DeployedItem.deployable_shield_generator, Set(Certification.AssaultEngineering))
|
Item(DeployedItem.deployable_shield_generator, Set(Certification.AssaultEngineering))
|
||||||
|
|
@ -9841,14 +9841,17 @@ object GlobalDefinitions {
|
||||||
capture_terminal.Name = "capture_terminal"
|
capture_terminal.Name = "capture_terminal"
|
||||||
capture_terminal.Damageable = false
|
capture_terminal.Damageable = false
|
||||||
capture_terminal.Repairable = false
|
capture_terminal.Repairable = false
|
||||||
|
capture_terminal.FacilityHackTime = 15.minutes
|
||||||
|
|
||||||
secondary_capture.Name = "secondary_capture"
|
secondary_capture.Name = "secondary_capture"
|
||||||
secondary_capture.Damageable = false
|
secondary_capture.Damageable = false
|
||||||
secondary_capture.Repairable = false
|
secondary_capture.Repairable = false
|
||||||
|
secondary_capture.FacilityHackTime = 1.nanosecond
|
||||||
|
|
||||||
vanu_control_console.Name = "vanu_control_console"
|
vanu_control_console.Name = "vanu_control_console"
|
||||||
vanu_control_console.Damageable = false
|
vanu_control_console.Damageable = false
|
||||||
vanu_control_console.Repairable = false
|
vanu_control_console.Repairable = false
|
||||||
|
vanu_control_console.FacilityHackTime = 10.minutes
|
||||||
|
|
||||||
lodestar_repair_terminal.Name = "lodestar_repair_terminal"
|
lodestar_repair_terminal.Name = "lodestar_repair_terminal"
|
||||||
lodestar_repair_terminal.Interval = 1000
|
lodestar_repair_terminal.Interval = 1000
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ package net.psforever.objects
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
trait OwnableByPlayer {
|
trait OwnableByPlayer {
|
||||||
private var owner: Option[PlanetSideGUID] = None
|
private var owner: Option[PlanetSideGUID] = None
|
||||||
private var ownerName: Option[String] = None
|
private var ownerName: Option[String] = None
|
||||||
|
private var originalOwnerName: Option[String] = None
|
||||||
|
|
||||||
def Owner: Option[PlanetSideGUID] = owner
|
def Owner: Option[PlanetSideGUID] = owner
|
||||||
|
|
||||||
|
|
@ -33,24 +34,27 @@ trait OwnableByPlayer {
|
||||||
owner match {
|
owner match {
|
||||||
case Some(_) =>
|
case Some(_) =>
|
||||||
ownerName = owner
|
ownerName = owner
|
||||||
|
originalOwnerName = originalOwnerName.orElse(owner)
|
||||||
case None =>
|
case None =>
|
||||||
ownerName = None
|
ownerName = None
|
||||||
}
|
}
|
||||||
OwnerName
|
OwnerName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def OriginalOwnerName: Option[String] = originalOwnerName
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* na
|
* na
|
||||||
* @param player na
|
* @param player na
|
||||||
* @return na
|
* @return na
|
||||||
*/
|
*/
|
||||||
def AssignOwnership(player: Player): OwnableByPlayer = AssignOwnership(Some(player))
|
def AssignOwnership(player: Player): OwnableByPlayer = AssignOwnership(Some(player))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* na
|
* na
|
||||||
* @param playerOpt na
|
* @param playerOpt na
|
||||||
* @return na
|
* @return na
|
||||||
*/
|
*/
|
||||||
def AssignOwnership(playerOpt: Option[Player]): OwnableByPlayer = {
|
def AssignOwnership(playerOpt: Option[Player]): OwnableByPlayer = {
|
||||||
playerOpt match {
|
playerOpt match {
|
||||||
case Some(player) =>
|
case Some(player) =>
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,6 @@ class Player(var avatar: Avatar)
|
||||||
var spectator: Boolean = false
|
var spectator: Boolean = false
|
||||||
var silenced: Boolean = false
|
var silenced: Boolean = false
|
||||||
var death_by: Int = 0
|
var death_by: Int = 0
|
||||||
var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(0L)
|
|
||||||
var lastShotSeq_time: Int = -1
|
var lastShotSeq_time: Int = -1
|
||||||
|
|
||||||
/** From PlanetsideAttributeMessage */
|
/** From PlanetsideAttributeMessage */
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,14 @@ object Players {
|
||||||
* @param medic the name of the player doing the reviving
|
* @param medic the name of the player doing the reviving
|
||||||
* @param item the tool being used to revive the target player
|
* @param item the tool being used to revive the target player
|
||||||
*/
|
*/
|
||||||
def FinishRevivingPlayer(target: Player, medic: String, item: Tool)(): Unit = {
|
def FinishRevivingPlayer(target: Player, medic: Player, item: Tool)(): Unit = {
|
||||||
val name = target.Name
|
val name = target.Name
|
||||||
log.info(s"$medic had revived $name")
|
val medicName = medic.Name
|
||||||
|
log.info(s"$medicName had revived $name")
|
||||||
|
//target.History(PlayerRespawn(PlayerSource(target), target.Zone, target.Position, Some(PlayerSource(medic))))
|
||||||
val magazine = item.Discharge(Some(25))
|
val magazine = item.Discharge(Some(25))
|
||||||
target.Zone.AvatarEvents ! AvatarServiceMessage(
|
target.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
medic,
|
medicName,
|
||||||
AvatarAction.SendResponse(
|
AvatarAction.SendResponse(
|
||||||
Service.defaultPlayerGUID,
|
Service.defaultPlayerGUID,
|
||||||
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine)
|
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine)
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
|
||||||
val damageToShields = originalShields - shields
|
val damageToShields = originalShields - shields
|
||||||
val damage = damageToHealth + damageToShields
|
val damage = damageToHealth + damageToShields
|
||||||
if (WillAffectTarget(target, damage, cause)) {
|
if (WillAffectTarget(target, damage, cause)) {
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
DamageLog(
|
DamageLog(
|
||||||
target,
|
target,
|
||||||
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// Copyright (c) 2021 PSForever
|
// Copyright (c) 2021 PSForever
|
||||||
package net.psforever.objects
|
package net.psforever.objects
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.equipment.{EffectTarget, TargetValidation}
|
import net.psforever.objects.equipment.{EffectTarget, TargetValidation}
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.{Vitality, VitalityDefinition}
|
import net.psforever.objects.vital.{Vitality, VitalityDefinition}
|
||||||
import net.psforever.objects.vital.base.DamageType
|
import net.psforever.objects.vital.base.DamageType
|
||||||
import net.psforever.objects.vital.etc.EmpReason
|
import net.psforever.objects.vital.etc.EmpReason
|
||||||
|
|
|
||||||
|
|
@ -590,13 +590,6 @@ object Vehicle {
|
||||||
*/
|
*/
|
||||||
final case class Reactivate()
|
final case class Reactivate()
|
||||||
|
|
||||||
/**
|
|
||||||
* A request has been made to charge this vehicle's shields.
|
|
||||||
* @see `FacilityBenefitShieldChargeRequestMessage`
|
|
||||||
* @param amount the number of points to charge
|
|
||||||
*/
|
|
||||||
final case class ChargeShields(amount: Int)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Following a successful shield charge tick, display the results of the update.
|
* Following a successful shield charge tick, display the results of the update.
|
||||||
* @see `FacilityBenefitShieldChargeRequestMessage`
|
* @see `FacilityBenefitShieldChargeRequestMessage`
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
package net.psforever.objects.avatar
|
package net.psforever.objects.avatar
|
||||||
|
|
||||||
import net.psforever.actors.session.AvatarActor
|
import net.psforever.actors.session.AvatarActor
|
||||||
|
import net.psforever.objects.avatar.scoring.ScoreCard
|
||||||
import net.psforever.objects.definition.{AvatarDefinition, BasicDefinition}
|
import net.psforever.objects.definition.{AvatarDefinition, BasicDefinition}
|
||||||
import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot}
|
import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot}
|
||||||
import net.psforever.objects.inventory.LocallyRegisteredInventory
|
import net.psforever.objects.inventory.LocallyRegisteredInventory
|
||||||
import net.psforever.objects.loadouts.{Loadout, SquadLoadout}
|
import net.psforever.objects.loadouts.{Loadout, SquadLoadout}
|
||||||
import net.psforever.objects.locker.{LockerContainer, LockerEquipment}
|
import net.psforever.objects.locker.{LockerContainer, LockerEquipment}
|
||||||
import net.psforever.objects.{GlobalDefinitions, OffhandEquipmentSlot}
|
import net.psforever.objects.{GlobalDefinitions, OffhandEquipmentSlot}
|
||||||
import net.psforever.packet.game.objectcreate.RibbonBars
|
import net.psforever.packet.game.objectcreate.{BasicCharacterData, RibbonBars}
|
||||||
import net.psforever.types._
|
import net.psforever.types._
|
||||||
import org.joda.time.{Duration, LocalDateTime, Seconds}
|
import org.joda.time.{Duration, LocalDateTime, Seconds}
|
||||||
|
|
||||||
|
|
@ -74,6 +75,10 @@ object Avatar {
|
||||||
GlobalDefinitions.super_staminakit -> 5.minutes // Temporary - Default value is 20 minutes
|
GlobalDefinitions.super_staminakit -> 5.minutes // Temporary - Default value is 20 minutes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def apply(charId: Int, name: String, faction: PlanetSideEmpire.Value, sex: CharacterSex, head: Int, voice: CharacterVoice.Value): Avatar = {
|
||||||
|
Avatar(charId, BasicCharacterData(name, faction, sex, head, voice))
|
||||||
|
}
|
||||||
|
|
||||||
def makeLocker(): LockerContainer = {
|
def makeLocker(): LockerContainer = {
|
||||||
new LockerContainer({
|
new LockerContainer({
|
||||||
val inv = new LocallyRegisteredInventory(numbers = 40150 until 40450) // TODO var bad
|
val inv = new LocallyRegisteredInventory(numbers = 40150 until 40450) // TODO var bad
|
||||||
|
|
@ -113,11 +118,7 @@ case class MemberLists(
|
||||||
case class Avatar(
|
case class Avatar(
|
||||||
/** unique identifier corresponding to a database table row index */
|
/** unique identifier corresponding to a database table row index */
|
||||||
id: Int,
|
id: Int,
|
||||||
name: String,
|
basic: BasicCharacterData,
|
||||||
faction: PlanetSideEmpire.Value,
|
|
||||||
sex: CharacterSex,
|
|
||||||
head: Int,
|
|
||||||
voice: CharacterVoice.Value,
|
|
||||||
bep: Long = 0,
|
bep: Long = 0,
|
||||||
cep: Long = 0,
|
cep: Long = 0,
|
||||||
stamina: Int = 100,
|
stamina: Int = 100,
|
||||||
|
|
@ -132,7 +133,8 @@ case class Avatar(
|
||||||
decoration: ProgressDecoration = ProgressDecoration(),
|
decoration: ProgressDecoration = ProgressDecoration(),
|
||||||
loadouts: Loadouts = Loadouts(),
|
loadouts: Loadouts = Loadouts(),
|
||||||
cooldowns: Cooldowns = Cooldowns(),
|
cooldowns: Cooldowns = Cooldowns(),
|
||||||
people: MemberLists = MemberLists()
|
people: MemberLists = MemberLists(),
|
||||||
|
scorecard: ScoreCard = new ScoreCard()
|
||||||
) {
|
) {
|
||||||
assert(bep >= 0)
|
assert(bep >= 0)
|
||||||
assert(cep >= 0)
|
assert(cep >= 0)
|
||||||
|
|
@ -140,6 +142,16 @@ case class Avatar(
|
||||||
val br: BattleRank = BattleRank.withExperience(bep)
|
val br: BattleRank = BattleRank.withExperience(bep)
|
||||||
val cr: CommandRank = CommandRank.withExperience(cep)
|
val cr: CommandRank = CommandRank.withExperience(cep)
|
||||||
|
|
||||||
|
def name: String = basic.name
|
||||||
|
|
||||||
|
def faction: PlanetSideEmpire.Value = basic.faction
|
||||||
|
|
||||||
|
def sex: CharacterSex = basic.sex
|
||||||
|
|
||||||
|
def head: Int = basic.head
|
||||||
|
|
||||||
|
def voice: CharacterVoice.Value = basic.voice
|
||||||
|
|
||||||
private def cooldown(
|
private def cooldown(
|
||||||
times: Map[String, LocalDateTime],
|
times: Map[String, LocalDateTime],
|
||||||
cooldowns: Map[BasicDefinition, FiniteDuration],
|
cooldowns: Map[BasicDefinition, FiniteDuration],
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ package net.psforever.objects.avatar
|
||||||
import akka.actor.{Actor, ActorRef, Props, typed}
|
import akka.actor.{Actor, ActorRef, Props, typed}
|
||||||
import net.psforever.actors.session.AvatarActor
|
import net.psforever.actors.session.AvatarActor
|
||||||
import net.psforever.login.WorldSession.{DropEquipmentFromInventory, HoldNewEquipmentUp, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
import net.psforever.login.WorldSession.{DropEquipmentFromInventory, HoldNewEquipmentUp, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
||||||
import net.psforever.objects.{Player, _}
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ballistics.PlayerSource
|
|
||||||
import net.psforever.objects.ce.Deployable
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.definition.DeployAnimation
|
import net.psforever.objects.definition.DeployAnimation
|
||||||
import net.psforever.objects.definition.converter.OCM
|
import net.psforever.objects.definition.converter.OCM
|
||||||
|
|
@ -33,6 +32,7 @@ import net.psforever.objects.locker.LockerContainerControl
|
||||||
import net.psforever.objects.serverobject.environment._
|
import net.psforever.objects.serverobject.environment._
|
||||||
import net.psforever.objects.serverobject.repair.Repairable
|
import net.psforever.objects.serverobject.repair.Repairable
|
||||||
import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad
|
import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.objects.vital.collision.CollisionReason
|
import net.psforever.objects.vital.collision.CollisionReason
|
||||||
import net.psforever.objects.vital.environment.EnvironmentReason
|
import net.psforever.objects.vital.environment.EnvironmentReason
|
||||||
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
|
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
|
||||||
|
|
@ -50,21 +50,21 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
with AggravatedBehavior
|
with AggravatedBehavior
|
||||||
with AuraEffectBehavior
|
with AuraEffectBehavior
|
||||||
with RespondsToZoneEnvironment {
|
with RespondsToZoneEnvironment {
|
||||||
def JammableObject = player
|
def JammableObject: Player = player
|
||||||
|
|
||||||
def DamageableObject = player
|
def DamageableObject: Player = player
|
||||||
|
|
||||||
def ContainerObject = player
|
def ContainerObject: Player = player
|
||||||
|
|
||||||
def AggravatedObject = player
|
def AggravatedObject: Player = player
|
||||||
|
|
||||||
def AuraTargetObject = player
|
def AuraTargetObject: Player = player
|
||||||
ApplicableEffect(Aura.Plasma)
|
ApplicableEffect(Aura.Plasma)
|
||||||
ApplicableEffect(Aura.Napalm)
|
ApplicableEffect(Aura.Napalm)
|
||||||
ApplicableEffect(Aura.Comet)
|
ApplicableEffect(Aura.Comet)
|
||||||
ApplicableEffect(Aura.Fire)
|
ApplicableEffect(Aura.Fire)
|
||||||
|
|
||||||
def InteractiveObject = player
|
def InteractiveObject: Player = player
|
||||||
SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
|
SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
|
||||||
SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
|
SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
|
||||||
SetInteraction(EnvironmentAttribute.Death, doInteractingWithDeath)
|
SetInteraction(EnvironmentAttribute.Death, doInteractingWithDeath)
|
||||||
|
|
@ -105,8 +105,18 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
case Player.Die(Some(reason)) =>
|
case Player.Die(Some(reason)) =>
|
||||||
if (player.isAlive) {
|
if (player.isAlive) {
|
||||||
//primary death
|
//primary death
|
||||||
PerformDamage(player, reason.calculate())
|
val health = player.Health
|
||||||
suicide()
|
val psource = PlayerSource(player)
|
||||||
|
player.Health = 0
|
||||||
|
HandleDamage(
|
||||||
|
player,
|
||||||
|
DamageResult(psource, psource.copy(health = 0), reason),
|
||||||
|
health,
|
||||||
|
damageToArmor = 0,
|
||||||
|
damageToStamina = 0,
|
||||||
|
damageToCapacitor = 0
|
||||||
|
)
|
||||||
|
damageLog.info(s"${player.Name}-infantry: dead by explicit reason - ${reason.cause.resolution}")
|
||||||
}
|
}
|
||||||
|
|
||||||
case Player.Die(None) =>
|
case Player.Die(None) =>
|
||||||
|
|
@ -138,12 +148,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth))
|
events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth))
|
||||||
player.History(
|
player.LogActivity(
|
||||||
HealFromEquipment(
|
HealFromEquipment(
|
||||||
PlayerSource(player),
|
|
||||||
PlayerSource(user),
|
PlayerSource(user),
|
||||||
newHealth - originalHealth,
|
GlobalDefinitions.medicalapplicator,
|
||||||
GlobalDefinitions.medicalapplicator
|
newHealth - originalHealth
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +181,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
) {
|
) {
|
||||||
sender() ! CommonMessages.Progress(
|
sender() ! CommonMessages.Progress(
|
||||||
4,
|
4,
|
||||||
Players.FinishRevivingPlayer(player, user.Name, item),
|
Players.FinishRevivingPlayer(player, user, item),
|
||||||
Players.RevivingTickAction(player, user, item)
|
Players.RevivingTickAction(player, user, item)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -202,12 +211,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 4, player.Armor))
|
events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 4, player.Armor))
|
||||||
player.History(
|
player.LogActivity(
|
||||||
RepairFromEquipment(
|
RepairFromEquipment(
|
||||||
PlayerSource(player),
|
|
||||||
PlayerSource(user),
|
PlayerSource(user),
|
||||||
newArmor - originalArmor,
|
GlobalDefinitions.bank,
|
||||||
GlobalDefinitions.bank
|
newArmor - originalArmor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +250,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
if (player.Health == player.MaxHealth) {
|
if (player.Health == player.MaxHealth) {
|
||||||
(None, 0, 0, "@HealComplete")
|
(None, 0, 0, "@HealComplete")
|
||||||
} else {
|
} else {
|
||||||
player.History(HealFromKit(PlayerSource(player), 25, kdef))
|
player.LogActivity(HealFromKit(kdef, 25))
|
||||||
player.Health = player.Health + 25
|
player.Health = player.Health + 25
|
||||||
(Some(index), 0, player.Health, "")
|
(Some(index), 0, player.Health, "")
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +258,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
if (player.Health == player.MaxHealth) {
|
if (player.Health == player.MaxHealth) {
|
||||||
(None, 0, 0, "@HealComplete")
|
(None, 0, 0, "@HealComplete")
|
||||||
} else {
|
} else {
|
||||||
player.History(HealFromKit(PlayerSource(player), 100, kdef))
|
player.LogActivity(HealFromKit(kdef, 100))
|
||||||
player.Health = player.Health + 100
|
player.Health = player.Health + 100
|
||||||
(Some(index), 0, player.Health, "")
|
(Some(index), 0, player.Health, "")
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +266,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
if (player.Armor == player.MaxArmor) {
|
if (player.Armor == player.MaxArmor) {
|
||||||
(None, 0, 0, "Armor at maximum - No repairing required.")
|
(None, 0, 0, "Armor at maximum - No repairing required.")
|
||||||
} else {
|
} else {
|
||||||
player.History(RepairFromKit(PlayerSource(player), 200, kdef))
|
player.LogActivity(RepairFromKit(kdef, 200))
|
||||||
player.Armor = player.Armor + 200
|
player.Armor = player.Armor + 200
|
||||||
(Some(index), 4, player.Armor, "")
|
(Some(index), 4, player.Armor, "")
|
||||||
}
|
}
|
||||||
|
|
@ -426,13 +434,14 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
val originalArmor = player.Armor
|
val originalArmor = player.Armor
|
||||||
player.ExoSuit = nextSuit
|
player.ExoSuit = nextSuit
|
||||||
val toMaxArmor = player.MaxArmor
|
val toMaxArmor = player.MaxArmor
|
||||||
val toArmor =
|
val toArmor = {
|
||||||
if (originalSuit != nextSuit || originalSubtype != nextSubtype || originalArmor > toMaxArmor) {
|
if (originalSuit != nextSuit || originalSubtype != nextSubtype || originalArmor > toMaxArmor) {
|
||||||
player.History(HealFromExoSuitChange(PlayerSource(player), nextSuit))
|
player.LogActivity(RepairFromExoSuitChange(nextSuit, toMaxArmor - player.Armor))
|
||||||
player.Armor = toMaxArmor
|
player.Armor = toMaxArmor
|
||||||
} else {
|
} else {
|
||||||
player.Armor = originalArmor
|
player.Armor = originalArmor
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//ensure arm is down, even if it needs to go back up
|
//ensure arm is down, even if it needs to go back up
|
||||||
if (player.DrawnSlot != Player.HandsDownSlot) {
|
if (player.DrawnSlot != Player.HandsDownSlot) {
|
||||||
player.DrawnSlot = Player.HandsDownSlot
|
player.DrawnSlot = Player.HandsDownSlot
|
||||||
|
|
@ -488,9 +497,9 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
)
|
)
|
||||||
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
player.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
player.Name,
|
player.Name,
|
||||||
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, true)
|
AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result=true)
|
||||||
)
|
)
|
||||||
case _ => assert(false, msg.toString)
|
case _ => assert(assertion=false, msg.toString)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Zone.Ground.ItemOnGround(item, _, _) =>
|
case Zone.Ground.ItemOnGround(item, _, _) =>
|
||||||
|
|
@ -528,8 +537,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
val trigger = new BoomerTrigger
|
val trigger = new BoomerTrigger
|
||||||
trigger.Companion = obj.GUID
|
trigger.Companion = obj.GUID
|
||||||
obj.Trigger = trigger
|
obj.Trigger = trigger
|
||||||
//TODO sufficiently delete the tool
|
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, tool.GUID))
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(player.GUID, tool.GUID))
|
|
||||||
TaskWorkflow.execute(GUIDTask.unregisterEquipment(zone.GUID, tool))
|
TaskWorkflow.execute(GUIDTask.unregisterEquipment(zone.GUID, tool))
|
||||||
player.Find(tool) match {
|
player.Find(tool) match {
|
||||||
case Some(index) if player.VisibleSlots.contains(index) =>
|
case Some(index) if player.VisibleSlots.contains(index) =>
|
||||||
|
|
@ -593,19 +601,18 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
val originalSubtype = Loadout.DetermineSubtype(player)
|
val originalSubtype = Loadout.DetermineSubtype(player)
|
||||||
val requestToChangeArmor = originalSuit != exosuit || originalSubtype != subtype
|
val requestToChangeArmor = originalSuit != exosuit || originalSubtype != subtype
|
||||||
val allowedToChangeArmor = Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
val allowedToChangeArmor = Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
||||||
(if (exosuit == ExoSuitType.MAX) {
|
(if (exosuit == ExoSuitType.MAX) {
|
||||||
val weapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
|
val weapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
|
||||||
player.avatar.purchaseCooldown(weapon) match {
|
player.avatar.purchaseCooldown(weapon) match {
|
||||||
case Some(_) =>
|
case Some(_) =>
|
||||||
false
|
false
|
||||||
case None =>
|
case None =>
|
||||||
avatarActor ! AvatarActor.UpdatePurchaseTime(weapon)
|
avatarActor ! AvatarActor.UpdatePurchaseTime(weapon)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
true
|
||||||
true
|
})
|
||||||
})
|
|
||||||
if (requestToChangeArmor && allowedToChangeArmor) {
|
if (requestToChangeArmor && allowedToChangeArmor) {
|
||||||
log.info(s"${player.Name} wants to change to a different exo-suit - $exosuit")
|
log.info(s"${player.Name} wants to change to a different exo-suit - $exosuit")
|
||||||
val beforeHolsters = Players.clearHolsters(player.Holsters().iterator)
|
val beforeHolsters = Players.clearHolsters(player.Holsters().iterator)
|
||||||
|
|
@ -614,13 +621,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
val originalArmor = player.Armor
|
val originalArmor = player.Armor
|
||||||
player.ExoSuit = exosuit //changes the value of MaxArmor to reflect the new exo-suit
|
player.ExoSuit = exosuit //changes the value of MaxArmor to reflect the new exo-suit
|
||||||
val toMaxArmor = player.MaxArmor
|
val toMaxArmor = player.MaxArmor
|
||||||
val toArmor = if (originalSuit != exosuit || originalSubtype != subtype || originalArmor > toMaxArmor) {
|
val toArmor = toMaxArmor
|
||||||
player.History(HealFromExoSuitChange(PlayerSource(player), exosuit))
|
if (originalArmor != toMaxArmor) {
|
||||||
player.Armor = toMaxArmor
|
player.LogActivity(RepairFromExoSuitChange(exosuit, toMaxArmor - originalArmor))
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.Armor = originalArmor
|
|
||||||
}
|
}
|
||||||
|
player.Armor = toMaxArmor
|
||||||
//ensure arm is down, even if it needs to go back up
|
//ensure arm is down, even if it needs to go back up
|
||||||
if (player.DrawnSlot != Player.HandsDownSlot) {
|
if (player.DrawnSlot != Player.HandsDownSlot) {
|
||||||
player.DrawnSlot = Player.HandsDownSlot
|
player.DrawnSlot = Player.HandsDownSlot
|
||||||
|
|
@ -804,7 +809,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
})) {
|
})) {
|
||||||
//activate second wind
|
//activate second wind
|
||||||
player.Health += 25
|
player.Health += 25
|
||||||
player.History(HealFromImplant(PlayerSource(player), 25, ImplantType.SecondWind))
|
player.LogActivity(HealFromImplant(ImplantType.SecondWind, 25))
|
||||||
avatarActor ! AvatarActor.ResetImplant(ImplantType.SecondWind)
|
avatarActor ! AvatarActor.ResetImplant(ImplantType.SecondWind)
|
||||||
avatarActor ! AvatarActor.RestoreStamina(25)
|
avatarActor ! AvatarActor.RestoreStamina(25)
|
||||||
}
|
}
|
||||||
|
|
@ -844,7 +849,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
cause.interaction.cause.source.Aggravated.nonEmpty
|
cause.interaction.cause.source.Aggravated.nonEmpty
|
||||||
}
|
}
|
||||||
//log historical event (always)
|
//log historical event (always)
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
//stat changes
|
//stat changes
|
||||||
if (damageToCapacitor > 0) {
|
if (damageToCapacitor > 0) {
|
||||||
events ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(
|
||||||
|
|
@ -959,7 +964,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
avatarActor ! AvatarActor.DeinitializeImplants()
|
avatarActor ! AvatarActor.DeinitializeImplants()
|
||||||
|
|
||||||
//log historical event
|
//log historical event
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
//log message
|
//log message
|
||||||
cause.adversarial match {
|
cause.adversarial match {
|
||||||
case Some(a) =>
|
case Some(a) =>
|
||||||
|
|
@ -1015,7 +1020,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
nameChannel,
|
nameChannel,
|
||||||
AvatarAction.SendResponse(
|
AvatarAction.SendResponse(
|
||||||
Service.defaultPlayerGUID,
|
Service.defaultPlayerGUID,
|
||||||
AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, target.Faction, true)
|
AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, target.Faction, unk5=true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
//TODO other methods of death?
|
//TODO other methods of death?
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.avatar.scoring
|
||||||
|
|
||||||
|
final case class EquipmentStat(objectId: Int, shotsFired: Int, shotsLanded: Int, kills: Int)
|
||||||
|
|
||||||
|
object EquipmentStat {
|
||||||
|
def apply(objectId: Int): EquipmentStat = EquipmentStat(objectId, 0, 1, 0)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.avatar.scoring
|
||||||
|
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
|
import net.psforever.objects.vital.interaction.DamageResult
|
||||||
|
import org.joda.time.LocalDateTime
|
||||||
|
|
||||||
|
trait KDAStat {
|
||||||
|
def experienceEarned: Long
|
||||||
|
val time: LocalDateTime = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
final case class Kill(victim: PlayerSource, info: DamageResult, experienceEarned: Long) extends KDAStat
|
||||||
|
|
||||||
|
final case class Assist(victim: PlayerSource, weapons: Seq[Int], damageInflictedPercentage: Float, experienceEarned: Long) extends KDAStat
|
||||||
|
|
||||||
|
final case class Death(assailant: Seq[PlayerSource], timeAlive: Long, bep: Long) extends KDAStat {
|
||||||
|
def experienceEarned: Long = 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.avatar.scoring
|
||||||
|
|
||||||
|
final case class Life(
|
||||||
|
kills: Seq[Kill],
|
||||||
|
assists: Seq[Assist],
|
||||||
|
death: Option[Death],
|
||||||
|
equipmentStats: Seq[EquipmentStat]
|
||||||
|
)
|
||||||
|
|
||||||
|
object Life {
|
||||||
|
def apply(): Life = Life(Nil, Nil, None, Nil)
|
||||||
|
|
||||||
|
def bep(life: Life): Long = {
|
||||||
|
life.kills.foldLeft(0L)(_ + _.experienceEarned) + life.assists.foldLeft(0L)(_ + _.experienceEarned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.avatar.scoring
|
||||||
|
|
||||||
|
import net.psforever.objects.GlobalDefinitions
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, StatisticalCategory}
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
class ScoreCard() {
|
||||||
|
private var curr: Life = Life()
|
||||||
|
private var lives: Seq[Life] = Seq()
|
||||||
|
private val killStatistics: mutable.HashMap[Int, Statistic] = mutable.HashMap[Int, Statistic]()
|
||||||
|
private val assistStatistics: mutable.HashMap[Int, Statistic] = mutable.HashMap[Int, Statistic]()
|
||||||
|
|
||||||
|
def CurrentLife: Life = curr
|
||||||
|
|
||||||
|
def Lives: Seq[Life] = lives
|
||||||
|
|
||||||
|
def KillStatistics: Map[Int, Statistic] = killStatistics.toMap
|
||||||
|
|
||||||
|
def AssistStatistics: Map[Int, Statistic] = assistStatistics.toMap
|
||||||
|
|
||||||
|
def rate(msg: Any): Unit = {
|
||||||
|
msg match {
|
||||||
|
case e: EquipmentStat =>
|
||||||
|
curr = ScoreCard.updateEquipmentStat(curr, e)
|
||||||
|
case k: Kill =>
|
||||||
|
curr = curr.copy(kills = k +: curr.kills)
|
||||||
|
curr = ScoreCard.updateEquipmentStat(curr, EquipmentStat(k.info.interaction.cause.attribution, 0, 0, 1))
|
||||||
|
ScoreCard.updateStatisticsFor(killStatistics, k.info.interaction.cause.attribution, k.victim.Faction)
|
||||||
|
case a: Assist =>
|
||||||
|
curr = curr.copy(assists = a +: curr.assists)
|
||||||
|
val faction = a.victim.Faction
|
||||||
|
a.weapons.foreach { wid =>
|
||||||
|
ScoreCard.updateStatisticsFor(assistStatistics, wid, faction)
|
||||||
|
}
|
||||||
|
case d: Death =>
|
||||||
|
val expired = curr
|
||||||
|
curr = Life()
|
||||||
|
lives = expired.copy(death = Some(d)) +: lives
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ScoreCard {
|
||||||
|
private def updateEquipmentStat(curr: Life, entry: EquipmentStat): Life = {
|
||||||
|
updateEquipmentStat(curr, entry, entry.objectId, entry.kills)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def updateEquipmentStat(
|
||||||
|
curr: Life,
|
||||||
|
entry: EquipmentStat,
|
||||||
|
objectId: Int,
|
||||||
|
killCount: Int
|
||||||
|
): Life = {
|
||||||
|
curr.equipmentStats.indexWhere { a => a.objectId == objectId } match {
|
||||||
|
case -1 =>
|
||||||
|
curr.copy(equipmentStats = entry +: curr.equipmentStats)
|
||||||
|
case index =>
|
||||||
|
val stats = curr.equipmentStats
|
||||||
|
val old = stats(index)
|
||||||
|
curr.copy(
|
||||||
|
equipmentStats = (stats.take(index) :+ old.copy(
|
||||||
|
shotsFired = old.shotsFired + entry.shotsFired,
|
||||||
|
shotsLanded = old.shotsLanded + entry.shotsLanded,
|
||||||
|
kills = old.kills + killCount
|
||||||
|
)) ++ stats.drop(index+1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
private def updateStatisticsFor(
|
||||||
|
statisticMap: mutable.HashMap[Int, Statistic],
|
||||||
|
objectId: Int,
|
||||||
|
victimFaction: PlanetSideEmpire.Value
|
||||||
|
): Statistic = {
|
||||||
|
statisticMap.get(objectId) match {
|
||||||
|
case Some(fields) =>
|
||||||
|
val outEntry = victimFaction match {
|
||||||
|
case PlanetSideEmpire.TR => fields.copy(tr_b = fields.tr_b + 1)
|
||||||
|
case PlanetSideEmpire.NC => fields.copy(nc_b = fields.nc_b + 1)
|
||||||
|
case PlanetSideEmpire.VS => fields.copy(vs_b = fields.vs_b + 1)
|
||||||
|
case PlanetSideEmpire.NEUTRAL => fields.copy(ps_b = fields.ps_b + 1)
|
||||||
|
}
|
||||||
|
outEntry
|
||||||
|
case _ =>
|
||||||
|
val out = Statistic(0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
statisticMap.put(objectId, out)
|
||||||
|
updateStatisticsFor(statisticMap, objectId, victimFaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def weaponObjectIdMap(objectId: Int): Int = {
|
||||||
|
objectId match {
|
||||||
|
//aphelion
|
||||||
|
case 81 | 82 => 80
|
||||||
|
case 90 | 92 => 88
|
||||||
|
case 94 | 95 => 93
|
||||||
|
case 102 | 104 => 100
|
||||||
|
case 107 | 109 => 105
|
||||||
|
//colossus
|
||||||
|
case 183 | 184 => 182
|
||||||
|
case 187 | 189 => 185
|
||||||
|
case 192 | 194 => 190
|
||||||
|
case 202 | 203 => 201
|
||||||
|
case 206 | 208 => 204
|
||||||
|
//cycler
|
||||||
|
case 234 | 235 | 236 => 233
|
||||||
|
//peregrine
|
||||||
|
case 634 | 635 => 633
|
||||||
|
case 638 | 640 => 636
|
||||||
|
case 646 | 648 => 644
|
||||||
|
case 650 | 651 => 649
|
||||||
|
case 660 | 662 => 658
|
||||||
|
//eh
|
||||||
|
case _ => objectId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def rewardKillGetCategories(victim: SourceEntry): Seq[StatisticalCategory] = {
|
||||||
|
victim match {
|
||||||
|
case p: PlayerSource =>
|
||||||
|
p.seatedIn match {
|
||||||
|
case Some((v: VehicleSource, seat: Int)) =>
|
||||||
|
val seatCategory = if (seat == 0) {
|
||||||
|
StatisticalCategory.DriverKilled
|
||||||
|
} else {
|
||||||
|
v.Definition.controlledWeapons().get(seat) match {
|
||||||
|
case Some(_) => StatisticalCategory.GunnerKilled
|
||||||
|
case None => StatisticalCategory.PassengerKilled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GlobalDefinitions.isFlightVehicle(v.Definition)) {
|
||||||
|
Seq(StatisticalCategory.Dogfighter, seatCategory)
|
||||||
|
} else {
|
||||||
|
Seq(seatCategory)
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
Seq(StatisticalCategory.Destroyed)
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
Seq(StatisticalCategory.Destroyed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.avatar.scoring
|
||||||
|
|
||||||
|
final case class Statistic(tr_a: Int, tr_b: Int, nc_a: Int, nc_b: Int, vs_a: Int, vs_b: Int, ps_a: Int, ps_b: Int)
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.ballistics
|
|
||||||
|
|
||||||
import net.psforever.objects.ce.Deployable
|
|
||||||
import net.psforever.objects.definition.{DeployableDefinition, ObjectDefinition}
|
|
||||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
|
||||||
|
|
||||||
final case class DeployableSource(
|
|
||||||
obj_def: ObjectDefinition with DeployableDefinition,
|
|
||||||
faction: PlanetSideEmpire.Value,
|
|
||||||
health: Int,
|
|
||||||
shields: Int,
|
|
||||||
owner: SourceEntry,
|
|
||||||
position: Vector3,
|
|
||||||
orientation: Vector3
|
|
||||||
) extends SourceEntry {
|
|
||||||
override def Name = obj_def.Descriptor
|
|
||||||
override def Faction = faction
|
|
||||||
def Definition: ObjectDefinition with DeployableDefinition = obj_def
|
|
||||||
def Health = health
|
|
||||||
def Shields = shields
|
|
||||||
def OwnerName = owner.Name
|
|
||||||
def Position = position
|
|
||||||
def Orientation = orientation
|
|
||||||
def Velocity = None
|
|
||||||
def Modifiers = obj_def.asInstanceOf[ResistanceProfile]
|
|
||||||
}
|
|
||||||
|
|
||||||
object DeployableSource {
|
|
||||||
def apply(obj: Deployable): DeployableSource = {
|
|
||||||
val ownerName = obj.OwnerName
|
|
||||||
val ownerSource = (obj.Zone.LivePlayers ++ obj.Zone.Corpses)
|
|
||||||
.find { p => ownerName.contains(p.Name) }
|
|
||||||
match {
|
|
||||||
case Some(p) => SourceEntry(p)
|
|
||||||
case _ => SourceEntry.None
|
|
||||||
}
|
|
||||||
DeployableSource(
|
|
||||||
obj.Definition,
|
|
||||||
obj.Faction,
|
|
||||||
obj.Health,
|
|
||||||
obj.Shields,
|
|
||||||
ownerSource,
|
|
||||||
obj.Position,
|
|
||||||
obj.Orientation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.ballistics
|
package net.psforever.objects.ballistics
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.base.DamageResolution
|
import net.psforever.objects.vital.base.DamageResolution
|
||||||
import net.psforever.objects.vital.etc.RadiationReason
|
import net.psforever.objects.vital.etc.RadiationReason
|
||||||
|
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.ballistics
|
|
||||||
|
|
||||||
import net.psforever.objects.PlanetSideGameObject
|
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
|
||||||
import net.psforever.objects.vital.VitalityDefinition
|
|
||||||
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
|
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
|
||||||
|
|
||||||
final case class ObjectSource(
|
|
||||||
obj: PlanetSideGameObject,
|
|
||||||
faction: PlanetSideEmpire.Value,
|
|
||||||
position: Vector3,
|
|
||||||
orientation: Vector3,
|
|
||||||
velocity: Option[Vector3]
|
|
||||||
) extends SourceEntry {
|
|
||||||
private val definition = obj.Definition match {
|
|
||||||
case vital : VitalityDefinition => vital
|
|
||||||
case genericDefinition => NonvitalDefinition(genericDefinition)
|
|
||||||
}
|
|
||||||
private val modifiers = definition match {
|
|
||||||
case nonvital : NonvitalDefinition => nonvital
|
|
||||||
case _ => ObjectSource.FixedResistances
|
|
||||||
}
|
|
||||||
override def Name = SourceEntry.NameFormat(obj.Definition.Name)
|
|
||||||
override def Faction = faction
|
|
||||||
def Definition = definition
|
|
||||||
def Position = position
|
|
||||||
def Orientation = orientation
|
|
||||||
def Velocity = velocity
|
|
||||||
def Modifiers = modifiers
|
|
||||||
}
|
|
||||||
|
|
||||||
object ObjectSource {
|
|
||||||
final val FixedResistances = new ResistanceProfileMutators() { }
|
|
||||||
|
|
||||||
def apply(obj: PlanetSideGameObject): ObjectSource = {
|
|
||||||
ObjectSource(
|
|
||||||
obj,
|
|
||||||
obj match {
|
|
||||||
case aligned: FactionAffinity => aligned.Faction
|
|
||||||
case _ => PlanetSideEmpire.NEUTRAL
|
|
||||||
},
|
|
||||||
obj.Position,
|
|
||||||
obj.Orientation,
|
|
||||||
obj.Velocity
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper for a definition that does not represent a `Vitality` object.
|
|
||||||
* @param definition the original definition
|
|
||||||
*/
|
|
||||||
class NonvitalDefinition(private val definition : ObjectDefinition)
|
|
||||||
extends ObjectDefinition(definition.ObjectId)
|
|
||||||
with ResistanceProfileMutators
|
|
||||||
with VitalityDefinition {
|
|
||||||
Name = { definition.Name }
|
|
||||||
Packet = { definition.Packet }
|
|
||||||
|
|
||||||
def canEqual(a: Any) : Boolean = a.isInstanceOf[definition.type]
|
|
||||||
|
|
||||||
override def equals(that: Any): Boolean = definition.equals(that)
|
|
||||||
|
|
||||||
override def hashCode: Int = definition.hashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
object NonvitalDefinition {
|
|
||||||
//single point of contact for all wrapped definitions
|
|
||||||
private val storage: scala.collection.mutable.LongMap[NonvitalDefinition] =
|
|
||||||
new scala.collection.mutable.LongMap[NonvitalDefinition]()
|
|
||||||
|
|
||||||
def apply(definition : ObjectDefinition) : NonvitalDefinition = {
|
|
||||||
storage.get(definition.ObjectId) match {
|
|
||||||
case Some(existing) =>
|
|
||||||
existing
|
|
||||||
case None =>
|
|
||||||
val out = new NonvitalDefinition(definition)
|
|
||||||
storage += definition.ObjectId.toLong -> out
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.ballistics
|
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
|
||||||
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition}
|
|
||||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
|
||||||
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
|
|
||||||
|
|
||||||
final case class PlayerSource(
|
|
||||||
name: String,
|
|
||||||
char_id: Long,
|
|
||||||
obj_def: AvatarDefinition,
|
|
||||||
faction: PlanetSideEmpire.Value,
|
|
||||||
exosuit: ExoSuitType.Value,
|
|
||||||
seated: Boolean,
|
|
||||||
health: Int,
|
|
||||||
armor: Int,
|
|
||||||
position: Vector3,
|
|
||||||
orientation: Vector3,
|
|
||||||
velocity: Option[Vector3],
|
|
||||||
crouching: Boolean,
|
|
||||||
jumping: Boolean,
|
|
||||||
modifiers: ResistanceProfile
|
|
||||||
) extends SourceEntry {
|
|
||||||
override def Name = name
|
|
||||||
override def Faction = faction
|
|
||||||
override def CharId = char_id
|
|
||||||
def Definition = obj_def
|
|
||||||
def ExoSuit = exosuit
|
|
||||||
def Seated = seated
|
|
||||||
def Health = health
|
|
||||||
def Armor = armor
|
|
||||||
def Position = position
|
|
||||||
def Orientation = orientation
|
|
||||||
def Velocity = velocity
|
|
||||||
def Modifiers = modifiers
|
|
||||||
}
|
|
||||||
|
|
||||||
object PlayerSource {
|
|
||||||
def apply(tplayer: Player): PlayerSource = {
|
|
||||||
PlayerSource(
|
|
||||||
tplayer.Name,
|
|
||||||
tplayer.CharId,
|
|
||||||
tplayer.Definition,
|
|
||||||
tplayer.Faction,
|
|
||||||
tplayer.ExoSuit,
|
|
||||||
tplayer.VehicleSeated.nonEmpty,
|
|
||||||
tplayer.Health,
|
|
||||||
tplayer.Armor,
|
|
||||||
tplayer.Position,
|
|
||||||
tplayer.Orientation,
|
|
||||||
tplayer.Velocity,
|
|
||||||
tplayer.Crouching,
|
|
||||||
tplayer.Jumping,
|
|
||||||
ExoSuitDefinition.Select(tplayer.ExoSuit, tplayer.Faction)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.ballistics
|
package net.psforever.objects.ballistics
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
//
|
||||||
import net.psforever.objects.PlanetSideGameObject
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
|
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
|
||||||
import net.psforever.objects.entity.SimpleWorldEntity
|
import net.psforever.objects.entity.SimpleWorldEntity
|
||||||
|
|
@ -13,42 +15,44 @@ import net.psforever.objects.zones.blockmap.BlockMapEntity
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A summation of weapon discharge.
|
* A summation of weapon discharge.
|
||||||
* @see `ProjectileDefinition`
|
* @see `ProjectileDefinition`
|
||||||
* @see `ToolDefinition`
|
* @see `ToolDefinition`
|
||||||
* @see `FireModeDefinition`
|
* @see `FireModeDefinition`
|
||||||
* @see `SourceEntry`
|
* @see `SourceEntry`
|
||||||
* @see `PlayerSource`
|
* @see `PlayerSource`
|
||||||
* @param profile an explanation of the damage that can be performed by this discharge
|
* @param profile an explanation of the damage that can be performed by this discharge
|
||||||
* @param tool_def the weapon that caused this discharge
|
* @param tool_def the weapon that caused this discharge
|
||||||
* @param fire_mode the current fire mode of the tool used
|
* @param fire_mode the current fire mode of the tool used
|
||||||
* @param owner the agency that caused the weapon to produce this projectile;
|
* @param mounted_in na
|
||||||
* most often a player (`PlayerSource`)
|
* @param owner the agency that caused the weapon to produce this projectile;
|
||||||
* @param attribute_to an object ID that refers to the method of death that would be reported;
|
* most often a player (`PlayerSource`)
|
||||||
* usually the same as `tool_def.ObjectId`;
|
* @param attribute_to an object ID that refers to the method of death that would be reported;
|
||||||
* if not, then it is a type of vehicle (and owner should have a positive `seated` field)
|
* usually the same as `tool_def.ObjectId`;
|
||||||
* @param shot_origin where the projectile started
|
* if not, then it is a type of vehicle (and owner should have a positive `seated` field)
|
||||||
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
* @param shot_origin where the projectile started
|
||||||
* @param shot_velocity the initial velocity coordinates of the projectile according to its world directions
|
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
||||||
* @param quality na
|
* @param shot_velocity the initial velocity coordinates of the projectile according to its world directions
|
||||||
* @param id an exclusive identifier for this projectile;
|
* @param quality na
|
||||||
* normally generated internally, but can be manually set (for modifying a continuous projectile reference)
|
* @param id an exclusive identifier for this projectile;
|
||||||
* @param fire_time when the weapon discharged was recorded;
|
* normally generated internally, but can be manually set (for modifying a continuous projectile reference)
|
||||||
* defaults to `System.currentTimeMillis()`
|
* @param fire_time when the weapon discharged was recorded;
|
||||||
*/
|
* defaults to `System.currentTimeMillis()`
|
||||||
|
*/
|
||||||
final case class Projectile(
|
final case class Projectile(
|
||||||
profile: ProjectileDefinition,
|
profile: ProjectileDefinition,
|
||||||
tool_def: ToolDefinition,
|
tool_def: ToolDefinition,
|
||||||
fire_mode: FireModeDefinition,
|
fire_mode: FireModeDefinition,
|
||||||
owner: SourceEntry,
|
mounted_in: Option[(Int, SourceEntry)],
|
||||||
attribute_to: Int,
|
owner: SourceEntry,
|
||||||
shot_origin: Vector3,
|
attribute_to: Int,
|
||||||
shot_angle: Vector3,
|
shot_origin: Vector3,
|
||||||
shot_velocity: Option[Vector3],
|
shot_angle: Vector3,
|
||||||
quality: ProjectileQuality = ProjectileQuality.Normal,
|
shot_velocity: Option[Vector3],
|
||||||
id: Long = Projectile.idGenerator.getAndIncrement(),
|
quality: ProjectileQuality = ProjectileQuality.Normal,
|
||||||
fire_time: Long = System.currentTimeMillis()
|
id: Long = Projectile.idGenerator.getAndIncrement(),
|
||||||
) extends PlanetSideGameObject
|
fire_time: Long = System.currentTimeMillis()
|
||||||
|
) extends PlanetSideGameObject
|
||||||
with BlockMapEntity {
|
with BlockMapEntity {
|
||||||
Position = shot_origin
|
Position = shot_origin
|
||||||
Orientation = shot_angle
|
Orientation = shot_angle
|
||||||
|
|
@ -66,18 +70,19 @@ final case class Projectile(
|
||||||
private var resolved: DamageResolution.Value = DamageResolution.Unresolved
|
private var resolved: DamageResolution.Value = DamageResolution.Unresolved
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a copy of this projectile with all the same information
|
* Create a copy of this projectile with all the same information
|
||||||
* save for the quality.
|
* save for the quality.
|
||||||
* Used mainly for aggravated damage.
|
* Used mainly for aggravated damage.
|
||||||
* It is important to note that the new projectile shares the (otherwise) exclusive id of the original.
|
* It is important to note that the new projectile shares the (otherwise) exclusive id of the original.
|
||||||
* @param value the new quality
|
* @param value the new quality
|
||||||
* @return a new `Projectile` entity
|
* @return a new `Projectile` entity
|
||||||
*/
|
*/
|
||||||
def quality(value: ProjectileQuality): Projectile = {
|
def quality(value: ProjectileQuality): Projectile = {
|
||||||
val projectile = Projectile(
|
val projectile = new Projectile(
|
||||||
profile,
|
profile,
|
||||||
tool_def,
|
tool_def,
|
||||||
fire_mode,
|
fire_mode,
|
||||||
|
mounted_in,
|
||||||
owner,
|
owner,
|
||||||
attribute_to,
|
attribute_to,
|
||||||
shot_origin,
|
shot_origin,
|
||||||
|
|
@ -93,8 +98,8 @@ final case class Projectile(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the projectile as being "encountered" or "managed" at least once.
|
* Mark the projectile as being "encountered" or "managed" at least once.
|
||||||
*/
|
*/
|
||||||
def Resolve(): Unit = {
|
def Resolve(): Unit = {
|
||||||
resolved = DamageResolution.Resolved
|
resolved = DamageResolution.Resolved
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +112,7 @@ final case class Projectile(
|
||||||
|
|
||||||
def isMiss: Boolean = resolved == DamageResolution.Missed
|
def isMiss: Boolean = resolved == DamageResolution.Missed
|
||||||
|
|
||||||
def Definition = profile
|
def Definition: ProjectileDefinition = profile
|
||||||
}
|
}
|
||||||
|
|
||||||
object Projectile {
|
object Projectile {
|
||||||
|
|
@ -116,22 +121,22 @@ object Projectile {
|
||||||
final val baseUID: Int = 40100
|
final val baseUID: Int = 40100
|
||||||
|
|
||||||
/** all clients progress through 40100 to 40124 normally, skipping only for long-lived projectiles
|
/** all clients progress through 40100 to 40124 normally, skipping only for long-lived projectiles
|
||||||
* 40125 to 40149 are being reserved as a guard against undetected overflow
|
* 40125 to 40149 are being reserved as a guard against undetected overflow
|
||||||
*/
|
*/
|
||||||
final val rangeUID: Int = 40150
|
final val rangeUID: Int = 40150
|
||||||
|
|
||||||
private val idGenerator: AtomicLong = new AtomicLong
|
private val idGenerator: AtomicLong = new AtomicLong
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor for an `Unresolved` projectile.
|
* Overloaded constructor for an `Unresolved` projectile.
|
||||||
* @param profile an explanation of the damage that can be performed by this discharge
|
* @param profile an explanation of the damage that can be performed by this discharge
|
||||||
* @param tool_def the weapon that caused this discharge
|
* @param tool_def the weapon that caused this discharge
|
||||||
* @param fire_mode the current fire mode of the tool used
|
* @param fire_mode the current fire mode of the tool used
|
||||||
* @param owner the agency that caused the weapon to produce this projectile
|
* @param owner the agency that caused the weapon to produce this projectile
|
||||||
* @param shot_origin where the projectile started
|
* @param shot_origin where the projectile started
|
||||||
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
||||||
* @return the `Projectile` object
|
* @return the `Projectile` object
|
||||||
*/
|
*/
|
||||||
def apply(
|
def apply(
|
||||||
profile: ProjectileDefinition,
|
profile: ProjectileDefinition,
|
||||||
tool_def: ToolDefinition,
|
tool_def: ToolDefinition,
|
||||||
|
|
@ -140,20 +145,20 @@ object Projectile {
|
||||||
shot_origin: Vector3,
|
shot_origin: Vector3,
|
||||||
shot_angle: Vector3
|
shot_angle: Vector3
|
||||||
): Projectile = {
|
): Projectile = {
|
||||||
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle, None)
|
Projectile(profile, tool_def, fire_mode, None, SourceEntry(owner), tool_def.ObjectId, shot_origin, shot_angle, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloaded constructor for an `Unresolved` projectile.
|
* Overloaded constructor for an `Unresolved` projectile.
|
||||||
* @param profile an explanation of the damage that can be performed by this discharge
|
* @param profile an explanation of the damage that can be performed by this discharge
|
||||||
* @param tool_def the weapon that caused this discharge
|
* @param tool_def the weapon that caused this discharge
|
||||||
* @param fire_mode the current fire mode of the tool used
|
* @param fire_mode the current fire mode of the tool used
|
||||||
* @param owner the agency that caused the weapon to produce this projectile
|
* @param owner the agency that caused the weapon to produce this projectile
|
||||||
* @param attribute_to an object ID that refers to the method of death that would be reported
|
* @param attribute_to an object ID that refers to the method of death that would be reported
|
||||||
* @param shot_origin where the projectile started
|
* @param shot_origin where the projectile started
|
||||||
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
* @param shot_angle in which direction the projectile was aimed when it was discharged
|
||||||
* @return the `Projectile` object
|
* @return the `Projectile` object
|
||||||
*/
|
*/
|
||||||
def apply(
|
def apply(
|
||||||
profile: ProjectileDefinition,
|
profile: ProjectileDefinition,
|
||||||
tool_def: ToolDefinition,
|
tool_def: ToolDefinition,
|
||||||
|
|
@ -163,7 +168,7 @@ object Projectile {
|
||||||
shot_origin: Vector3,
|
shot_origin: Vector3,
|
||||||
shot_angle: Vector3
|
shot_angle: Vector3
|
||||||
): Projectile = {
|
): Projectile = {
|
||||||
Projectile(profile, tool_def, fire_mode, SourceEntry(owner), attribute_to, shot_origin, shot_angle, None)
|
Projectile(profile, tool_def, fire_mode, None, SourceEntry(owner), attribute_to, shot_origin, shot_angle, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
|
|
@ -175,6 +180,6 @@ object Projectile {
|
||||||
shot_origin: Vector3,
|
shot_origin: Vector3,
|
||||||
shot_angle: Vector3
|
shot_angle: Vector3
|
||||||
): Projectile = {
|
): Projectile = {
|
||||||
Projectile(profile, tool_def, fire_mode, owner, attribute_to, shot_origin, shot_angle, None)
|
Projectile(profile, tool_def, fire_mode, None, owner, attribute_to, shot_origin, shot_angle, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.ballistics
|
|
||||||
|
|
||||||
import net.psforever.objects.ce.Deployable
|
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
|
|
||||||
import net.psforever.objects.entity.WorldEntity
|
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
|
||||||
import net.psforever.objects.vital.VitalityDefinition
|
|
||||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
|
||||||
|
|
||||||
trait SourceEntry extends WorldEntity {
|
|
||||||
def Name: String = ""
|
|
||||||
def Definition: ObjectDefinition with VitalityDefinition
|
|
||||||
def CharId: Long = 0L
|
|
||||||
def Faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
|
||||||
def Position_=(pos: Vector3) = Position
|
|
||||||
def Orientation_=(pos: Vector3) = Position
|
|
||||||
def Velocity_=(pos: Option[Vector3]) = Velocity
|
|
||||||
def Modifiers: ResistanceProfile
|
|
||||||
}
|
|
||||||
|
|
||||||
object SourceEntry {
|
|
||||||
final val None = new SourceEntry() {
|
|
||||||
def Definition = null
|
|
||||||
def Position = Vector3.Zero
|
|
||||||
def Orientation = Vector3.Zero
|
|
||||||
def Velocity = Some(Vector3.Zero)
|
|
||||||
def Modifiers = null
|
|
||||||
}
|
|
||||||
|
|
||||||
def apply(target: PlanetSideGameObject with FactionAffinity): SourceEntry = {
|
|
||||||
target match {
|
|
||||||
case obj: Player => PlayerSource(obj)
|
|
||||||
case obj: Vehicle => VehicleSource(obj)
|
|
||||||
case obj: Deployable => DeployableSource(obj)
|
|
||||||
case _ => ObjectSource(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def NameFormat(name: String): String = {
|
|
||||||
name
|
|
||||||
.replace("_", " ")
|
|
||||||
.split(" ")
|
|
||||||
.map(_.capitalize)
|
|
||||||
.mkString(" ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.objects.ballistics
|
|
||||||
|
|
||||||
import net.psforever.objects.Vehicle
|
|
||||||
import net.psforever.objects.definition.VehicleDefinition
|
|
||||||
import net.psforever.objects.vital.resistance.ResistanceProfile
|
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
|
||||||
|
|
||||||
final case class VehicleSource(
|
|
||||||
obj_def: VehicleDefinition,
|
|
||||||
faction: PlanetSideEmpire.Value,
|
|
||||||
health: Int,
|
|
||||||
shields: Int,
|
|
||||||
position: Vector3,
|
|
||||||
orientation: Vector3,
|
|
||||||
velocity: Option[Vector3],
|
|
||||||
occupants: List[SourceEntry],
|
|
||||||
modifiers: ResistanceProfile
|
|
||||||
) extends SourceEntry {
|
|
||||||
override def Name = SourceEntry.NameFormat(obj_def.Name)
|
|
||||||
override def Faction = faction
|
|
||||||
def Definition: VehicleDefinition = obj_def
|
|
||||||
def Health = health
|
|
||||||
def Shields = shields
|
|
||||||
def Position = position
|
|
||||||
def Orientation = orientation
|
|
||||||
def Velocity = velocity
|
|
||||||
def Modifiers = modifiers
|
|
||||||
}
|
|
||||||
|
|
||||||
object VehicleSource {
|
|
||||||
def apply(obj: Vehicle): VehicleSource = {
|
|
||||||
VehicleSource(
|
|
||||||
obj.Definition,
|
|
||||||
obj.Faction,
|
|
||||||
obj.Health,
|
|
||||||
obj.Shields,
|
|
||||||
obj.Position,
|
|
||||||
obj.Orientation,
|
|
||||||
obj.Velocity,
|
|
||||||
obj.Seats.values.map { seat =>
|
|
||||||
seat.occupant match {
|
|
||||||
case Some(p) => PlayerSource(p)
|
|
||||||
case _ => SourceEntry.None
|
|
||||||
}
|
|
||||||
}.toList,
|
|
||||||
obj.Definition.asInstanceOf[ResistanceProfile]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -65,7 +65,7 @@ object AvatarConverter {
|
||||||
def MakeAppearanceData(obj: Player): Int => CharacterAppearanceData = {
|
def MakeAppearanceData(obj: Player): Int => CharacterAppearanceData = {
|
||||||
val alt_model_flag: Boolean = obj.isBackpack
|
val alt_model_flag: Boolean = obj.isBackpack
|
||||||
val aa: Int => CharacterAppearanceA = CharacterAppearanceA(
|
val aa: Int => CharacterAppearanceA = CharacterAppearanceA(
|
||||||
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, obj.Voice),
|
obj.avatar.basic,
|
||||||
CommonFieldData(
|
CommonFieldData(
|
||||||
obj.Faction,
|
obj.Faction,
|
||||||
bops = false,
|
bops = false,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.definition.converter
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
import net.psforever.objects.serverobject.llu.CaptureFlag
|
import net.psforever.objects.serverobject.llu.CaptureFlag
|
||||||
import net.psforever.objects.serverobject.structures.Building
|
import net.psforever.objects.serverobject.structures.Building
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.packet.game.objectcreate.{CaptureFlagData, PlacementData}
|
import net.psforever.packet.game.objectcreate.{CaptureFlagData, PlacementData}
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
|
|
@ -13,7 +14,7 @@ class CaptureFlagConverter extends ObjectCreateConverter[CaptureFlag]() {
|
||||||
override def ConstructorData(obj : CaptureFlag) : Try[CaptureFlagData] = {
|
override def ConstructorData(obj : CaptureFlag) : Try[CaptureFlagData] = {
|
||||||
val hackInfo = obj.Owner.asInstanceOf[Building].CaptureTerminal.get.HackedBy match {
|
val hackInfo = obj.Owner.asInstanceOf[Building].CaptureTerminal.get.HackedBy match {
|
||||||
case Some(hackInfo) => hackInfo
|
case Some(hackInfo) => hackInfo
|
||||||
case _ => Hackable.HackInfo("", PlanetSideGUID(0), PlanetSideEmpire.NEUTRAL, Vector3.Zero, 0L, 0L)
|
case _ => Hackable.HackInfo(PlayerSource("", PlanetSideEmpire.NEUTRAL, Vector3.Zero), PlanetSideGUID(0), 0L, 0L)
|
||||||
}
|
}
|
||||||
|
|
||||||
val millisecondsRemaining = TimeUnit.MILLISECONDS.convert(math.max(0, hackInfo.hackStartTime + hackInfo.hackDuration - System.nanoTime), TimeUnit.NANOSECONDS)
|
val millisecondsRemaining = TimeUnit.MILLISECONDS.convert(math.max(0, hackInfo.hackStartTime + hackInfo.hackDuration - System.nanoTime), TimeUnit.NANOSECONDS)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class CharacterSelectConverter extends AvatarConverter {
|
||||||
*/
|
*/
|
||||||
private def MakeAppearanceData(obj: Player): Int => CharacterAppearanceData = {
|
private def MakeAppearanceData(obj: Player): Int => CharacterAppearanceData = {
|
||||||
val aa: Int => CharacterAppearanceA = CharacterAppearanceA(
|
val aa: Int => CharacterAppearanceA = CharacterAppearanceA(
|
||||||
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, CharacterVoice.Mute),
|
obj.avatar.basic,
|
||||||
CommonFieldData(
|
CommonFieldData(
|
||||||
obj.Faction,
|
obj.Faction,
|
||||||
bops = false,
|
bops = false,
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class CorpseConverter extends AvatarConverter {
|
||||||
*/
|
*/
|
||||||
private def MakeAppearanceData(obj: Player): Int => CharacterAppearanceData = {
|
private def MakeAppearanceData(obj: Player): Int => CharacterAppearanceData = {
|
||||||
val aa: Int => CharacterAppearanceA = CharacterAppearanceA(
|
val aa: Int => CharacterAppearanceA = CharacterAppearanceA(
|
||||||
BasicCharacterData(obj.Name, obj.Faction, CharacterSex.Male, 0, CharacterVoice.Mute),
|
obj.avatar.basic,
|
||||||
CommonFieldData(
|
CommonFieldData(
|
||||||
obj.Faction,
|
obj.Faction,
|
||||||
bops = false,
|
bops = false,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
package net.psforever.objects.equipment
|
package net.psforever.objects.equipment
|
||||||
|
|
||||||
import akka.actor.{Actor, Cancellable}
|
import akka.actor.{Actor, Cancellable}
|
||||||
import net.psforever.objects.ballistics.VehicleSource
|
|
||||||
import net.psforever.objects.{GlobalDefinitions, Tool, Vehicle}
|
import net.psforever.objects.{GlobalDefinitions, Tool, Vehicle}
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.damage.Damageable
|
import net.psforever.objects.serverobject.damage.Damageable
|
||||||
|
import net.psforever.objects.sourcing.VehicleSource
|
||||||
import net.psforever.objects.vital.RepairFromArmorSiphon
|
import net.psforever.objects.vital.RepairFromArmorSiphon
|
||||||
import net.psforever.objects.vital.etc.{ArmorSiphonModifiers, ArmorSiphonReason}
|
import net.psforever.objects.vital.etc.{ArmorSiphonModifiers, ArmorSiphonReason}
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
|
|
@ -76,7 +76,7 @@ object ArmorSiphonBehavior {
|
||||||
if before < obj.MaxHealth =>
|
if before < obj.MaxHealth =>
|
||||||
val after = obj.Health += amount
|
val after = obj.Health += amount
|
||||||
if(before < after) {
|
if(before < after) {
|
||||||
obj.History(RepairFromArmorSiphon(asr.siphon.Definition, before - after))
|
obj.LogActivity(RepairFromArmorSiphon(asr.siphon.Definition, VehicleSource(obj), before - after))
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
zone.id,
|
zone.id,
|
||||||
|
|
|
||||||
|
|
@ -39,22 +39,38 @@ object EffectTarget {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To repair at this silo, the vehicle:
|
||||||
|
* can not be a flight vehicle,
|
||||||
|
* must have some health already, but does not have all its health,
|
||||||
|
* and can not have taken damage in the last five seconds.
|
||||||
|
*/
|
||||||
def RepairSilo(target: PlanetSideGameObject): Boolean =
|
def RepairSilo(target: PlanetSideGameObject): Boolean =
|
||||||
target match {
|
target match {
|
||||||
case v: Vehicle =>
|
case v: Vehicle => !GlobalDefinitions.isFlightVehicle(v.Definition) && CommonRepairConditions(v)
|
||||||
!GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth && !v.History.takeWhile(System.currentTimeMillis() - _.time <= 5000L).exists(_.isInstanceOf[DamagingActivity])
|
case _ => false
|
||||||
case _ =>
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To repair at this landing pad, the vehicle:
|
||||||
|
* be a flight vehicle,
|
||||||
|
* must have some health already, but does not have all its health,
|
||||||
|
* and can not have taken damage in the last five seconds.
|
||||||
|
*/
|
||||||
def PadLanding(target: PlanetSideGameObject): Boolean =
|
def PadLanding(target: PlanetSideGameObject): Boolean =
|
||||||
target match {
|
target match {
|
||||||
case v: Vehicle =>
|
case v: Vehicle => GlobalDefinitions.isFlightVehicle(v.Definition) && CommonRepairConditions(v)
|
||||||
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth && !v.History.takeWhile(System.currentTimeMillis() - _.time <= 5000L).exists(_.isInstanceOf[DamagingActivity])
|
case _ => false
|
||||||
case _ =>
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def CommonRepairConditions(v: Vehicle): Boolean = {
|
||||||
|
v.Health > 0 && v.Health < v.MaxHealth &&
|
||||||
|
v.History.findLast { entry => entry.isInstanceOf[DamagingActivity] }.exists {
|
||||||
|
case entry if System.currentTimeMillis() - entry.time < 5000L => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def Player(target: PlanetSideGameObject): Boolean =
|
def Player(target: PlanetSideGameObject): Boolean =
|
||||||
target match {
|
target match {
|
||||||
case p: Player =>
|
case p: Player =>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Copyright (c) 2021 PSForever
|
// Copyright (c) 2021 PSForever
|
||||||
package net.psforever.objects.geometry
|
package net.psforever.objects.geometry
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, Projectile, SourceEntry}
|
import net.psforever.objects.ballistics.Projectile
|
||||||
import net.psforever.objects.geometry.d3._
|
import net.psforever.objects.geometry.d3._
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player}
|
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player}
|
||||||
import net.psforever.types.{ExoSuitType, Vector3}
|
import net.psforever.types.{ExoSuitType, Vector3}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject
|
package net.psforever.objects.serverobject
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
|
|
||||||
//temporary location for these messages
|
//temporary location for these messages
|
||||||
|
|
@ -23,4 +23,15 @@ object CommonMessages {
|
||||||
final case class Progress(delta: Float, completionAction: () => Unit, tickAction: Float => Boolean) {
|
final case class Progress(delta: Float, completionAction: () => Unit, tickAction: Float => Boolean) {
|
||||||
assert(delta > 0, s"progress activity change value must be positive number - $delta")
|
assert(delta > 0, s"progress activity change value must be positive number - $delta")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request has been made to charge this entity's shields.
|
||||||
|
* @see `FacilityBenefitShieldChargeRequestMessage`
|
||||||
|
* @param amount the number of points to charge
|
||||||
|
* @param motivator the element that caused the shield to charge;
|
||||||
|
* allowed to be `None`;
|
||||||
|
* most often, a `Building`;
|
||||||
|
* if the vehicle instigated its own charge (battleframe robotics), specify that
|
||||||
|
*/
|
||||||
|
final case class ChargeShields(amount: Int, motivator: Option[PlanetSideGameObject])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.damage
|
||||||
import akka.actor.{Actor, Cancellable}
|
import akka.actor.{Actor, Cancellable}
|
||||||
import net.psforever.objects.ballistics._
|
import net.psforever.objects.ballistics._
|
||||||
import net.psforever.objects.serverobject.aura.Aura
|
import net.psforever.objects.serverobject.aura.Aura
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.base._
|
import net.psforever.objects.vital.base._
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ trait DamageableAmenity extends DamageableEntity {
|
||||||
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
|
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
|
||||||
super.DestructionAwareness(target, cause)
|
super.DestructionAwareness(target, cause)
|
||||||
DamageableAmenity.DestructionAwareness(target, cause)
|
DamageableAmenity.DestructionAwareness(target, cause)
|
||||||
target.ClearHistory()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ trait DamageableEntity extends Damageable {
|
||||||
val health = target.Health
|
val health = target.Health
|
||||||
val damage = originalHealth - health
|
val damage = originalHealth - health
|
||||||
if (WillAffectTarget(target, damage, cause)) {
|
if (WillAffectTarget(target, damage, cause)) {
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
DamageLog(target, s"BEFORE=$originalHealth, AFTER=$health, CHANGE=$damage")
|
DamageLog(target, s"BEFORE=$originalHealth, AFTER=$health, CHANGE=$damage")
|
||||||
HandleDamage(target, cause, damage)
|
HandleDamage(target, cause, damage)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
package net.psforever.objects.serverobject.damage
|
package net.psforever.objects.serverobject.damage
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, SourceEntry}
|
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.packet.game.DamageWithPositionMessage
|
import net.psforever.packet.game.DamageWithPositionMessage
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ trait DamageableVehicle
|
||||||
//bfrs undergo a shiver spell before exploding
|
//bfrs undergo a shiver spell before exploding
|
||||||
val obj = DamageableObject
|
val obj = DamageableObject
|
||||||
obj.Health = 0
|
obj.Health = 0
|
||||||
obj.History(cause)
|
obj.LogActivity(cause)
|
||||||
DestructionAwareness(obj, cause)
|
DestructionAwareness(obj, cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ trait DamageableVehicle
|
||||||
val damageToHealth = originalHealth - health
|
val damageToHealth = originalHealth - health
|
||||||
val damageToShields = originalShields - shields
|
val damageToShields = originalShields - shields
|
||||||
if (WillAffectTarget(target, damageToHealth + damageToShields, cause)) {
|
if (WillAffectTarget(target, damageToHealth + damageToShields, cause)) {
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
DamageLog(
|
DamageLog(
|
||||||
target,
|
target,
|
||||||
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
|
||||||
|
|
@ -130,7 +130,7 @@ trait DamageableVehicle
|
||||||
|
|
||||||
if (obj.MountedIn.nonEmpty) {
|
if (obj.MountedIn.nonEmpty) {
|
||||||
//log historical event
|
//log historical event
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
}
|
}
|
||||||
//damage
|
//damage
|
||||||
if (Damageable.CanDamageOrJammer(target, totalDamage, cause.interaction)) {
|
if (Damageable.CanDamageOrJammer(target, totalDamage, cause.interaction)) {
|
||||||
|
|
@ -214,7 +214,6 @@ trait DamageableVehicle
|
||||||
}
|
}
|
||||||
//clean up
|
//clean up
|
||||||
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
|
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
|
||||||
target.ClearHistory()
|
|
||||||
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ trait DamageableWeaponTurret
|
||||||
}
|
}
|
||||||
|
|
||||||
//log historical event
|
//log historical event
|
||||||
target.History(cause)
|
target.LogActivity(cause)
|
||||||
//damage
|
//damage
|
||||||
if (Damageable.CanDamageOrJammer(target, damageToHealth, cause.interaction)) {
|
if (Damageable.CanDamageOrJammer(target, damageToHealth, cause.interaction)) {
|
||||||
//jammering
|
//jammering
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ class GeneratorControl(gen: Generator)
|
||||||
with DamageableEntity
|
with DamageableEntity
|
||||||
with RepairableEntity
|
with RepairableEntity
|
||||||
with AmenityAutoRepair {
|
with AmenityAutoRepair {
|
||||||
def FactionObject = gen
|
def FactionObject: Generator = gen
|
||||||
def DamageableObject = gen
|
def DamageableObject: Generator = gen
|
||||||
def RepairableObject = gen
|
def RepairableObject: Generator = gen
|
||||||
def AutoRepairObject = gen
|
def AutoRepairObject: Generator = gen
|
||||||
/** flagged to explode after some time */
|
/** flagged to explode after some time */
|
||||||
var imminentExplosion: Boolean = false
|
var imminentExplosion: Boolean = false
|
||||||
/** explode when this timer completes */
|
/** explode when this timer completes */
|
||||||
|
|
@ -123,9 +123,9 @@ class GeneratorControl(gen: Generator)
|
||||||
imminentExplosion = false
|
imminentExplosion = false
|
||||||
//hate on everything nearby
|
//hate on everything nearby
|
||||||
Zone.serverSideDamage(gen.Zone, gen, Zone.explosionDamage(gen.LastDamage), explosionFunc)
|
Zone.serverSideDamage(gen.Zone, gen, Zone.explosionDamage(gen.LastDamage), explosionFunc)
|
||||||
gen.ClearHistory()
|
|
||||||
|
|
||||||
case GeneratorControl.Restored() =>
|
case GeneratorControl.Restored() =>
|
||||||
|
gen.ClearHistory()
|
||||||
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Online))
|
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Online))
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -153,7 +153,6 @@ class GeneratorControl(gen: Generator)
|
||||||
imminentExplosion = false
|
imminentExplosion = false
|
||||||
gen.Condition = PlanetSideGeneratorState.Destroyed
|
gen.Condition = PlanetSideGeneratorState.Destroyed
|
||||||
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destroyed))
|
GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destroyed))
|
||||||
gen.ClearHistory()
|
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.hackable
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable.HackInfo
|
import net.psforever.objects.serverobject.hackable.Hackable.HackInfo
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.packet.game.TriggeredSound
|
import net.psforever.packet.game.TriggeredSound
|
||||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
|
|
@ -24,14 +25,14 @@ trait Hackable {
|
||||||
def HackedBy_=(agent: Option[Player]): Option[HackInfo] = {
|
def HackedBy_=(agent: Option[Player]): Option[HackInfo] = {
|
||||||
(hackedBy, agent) match {
|
(hackedBy, agent) match {
|
||||||
case (None, Some(actor)) =>
|
case (None, Some(actor)) =>
|
||||||
hackedBy = Some(HackInfo(actor.Name, actor.GUID, actor.Faction, actor.Position, System.nanoTime, 0L))
|
hackedBy = Some(HackInfo(PlayerSource(actor), actor.GUID, System.nanoTime, 0L))
|
||||||
case (Some(info), Some(actor)) =>
|
case (Some(info), Some(actor)) =>
|
||||||
if (actor.Faction == this.Faction) {
|
if (actor.Faction == this.Faction) {
|
||||||
//hack cleared
|
//hack cleared
|
||||||
hackedBy = None
|
hackedBy = None
|
||||||
} else if (actor.Faction != info.hackerFaction) {
|
} else if (actor.Faction != info.hackerFaction) {
|
||||||
//override the hack state with a new hack state if the new user has different faction affiliation
|
//override the hack state with a new hack state if the new user has different faction affiliation
|
||||||
hackedBy = Some(HackInfo(actor.Name, actor.GUID, actor.Faction, actor.Position, System.nanoTime, 0L))
|
hackedBy = Some(HackInfo(PlayerSource(actor), actor.GUID, System.nanoTime, 0L))
|
||||||
}
|
}
|
||||||
case (_, None) =>
|
case (_, None) =>
|
||||||
hackedBy = None
|
hackedBy = None
|
||||||
|
|
@ -67,30 +68,19 @@ trait Hackable {
|
||||||
hackDuration = arr
|
hackDuration = arr
|
||||||
arr
|
arr
|
||||||
}
|
}
|
||||||
|
|
||||||
// private var hackable : Option[Boolean] = None
|
|
||||||
// def Hackable : Boolean = hackable.getOrElse(Definition.Hackable)
|
|
||||||
//
|
|
||||||
// def Hackable_=(state : Boolean) : Boolean = Hackable_=(Some(state))
|
|
||||||
//
|
|
||||||
// def Hackable_=(state : Option[Boolean]) : Boolean = {
|
|
||||||
// hackable = state
|
|
||||||
// Hackable
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// def Definition : HackableDefinition
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Hackable {
|
object Hackable {
|
||||||
final case class HackInfo(
|
final case class HackInfo(
|
||||||
hackerName: String,
|
player: PlayerSource,
|
||||||
hackerGUID: PlanetSideGUID,
|
hackerGUID: PlanetSideGUID,
|
||||||
hackerFaction: PlanetSideEmpire.Value,
|
hackStartTime: Long,
|
||||||
hackerPos: Vector3,
|
hackDuration: Long
|
||||||
hackStartTime: Long,
|
) {
|
||||||
hackDuration: Long
|
def hackerName: String = player.Name
|
||||||
) {
|
def hackerFaction: PlanetSideEmpire.Value = player.Faction
|
||||||
def Duration(time: Long): HackInfo =
|
def hackerPos: Vector3 = player.Position
|
||||||
HackInfo(hackerName, hackerGUID, hackerFaction, hackerPos, hackStartTime, time)
|
|
||||||
|
def Duration(time: Long): HackInfo = HackInfo(player, hackerGUID, hackStartTime, time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Copyright (c) 2021 PSForever
|
||||||
package net.psforever.objects.serverobject.llu
|
package net.psforever.objects.serverobject.llu
|
||||||
|
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
|
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
|
||||||
|
|
@ -5,9 +6,28 @@ import net.psforever.objects.{GlobalDefinitions, Player}
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This object represents the LLU that gets spawned at a LLU socket when a LLU control console is hacked
|
* Represent a special entity that is carried by the player in certain circumstances.
|
||||||
*/
|
* The entity is not a piece of `Equipment` so it does not go into the holsters,
|
||||||
class CaptureFlag(tDef: CaptureFlagDefinition) extends Amenity {
|
* doe not into the player's inventory,
|
||||||
|
* and is not carried in or manipulated by the player's hands.
|
||||||
|
* The different game elements it simulates are:
|
||||||
|
* a facility's lattice logic unit (LLU),
|
||||||
|
* the cavern modules,
|
||||||
|
* and the rabbit ball (special game mode).<br>
|
||||||
|
* <br>
|
||||||
|
* For the lattice logic unit, when a facility is set to generate an LLU upon hack,
|
||||||
|
* and an adjacent facility on the lattice provides an accommodating faction connection,
|
||||||
|
* the unit gets spawned at the LLU socket within the hacked facility.
|
||||||
|
* The LLU socket actually doesn't do anything but keep track of the spawned flag and provide a location.
|
||||||
|
* It associates with the faction of the hacker and, carried by other players of the same faction only,
|
||||||
|
* must be brought to the control console of a designated facility that is owned by the faction of the hacking empire.
|
||||||
|
* If the hack is cancelled through a resecure, the LLU despawns.
|
||||||
|
* If the facility is counter-hacked, the active LLU despawns and a new LLU is spawned in the socket.
|
||||||
|
* Other empires can not interact with the LLU while it is dropped on the ground and
|
||||||
|
* vehicles will be warned and then deconstructed if they linges too long near a dropped LLU.
|
||||||
|
* The LLU can not be submerged in water or it will despawn and the hack will cancel.
|
||||||
|
*/
|
||||||
|
class CaptureFlag(private val tDef: CaptureFlagDefinition) extends Amenity {
|
||||||
def Definition : CaptureFlagDefinition = tDef
|
def Definition : CaptureFlagDefinition = tDef
|
||||||
|
|
||||||
private var target: Building = Building.NoBuilding
|
private var target: Building = Building.NoBuilding
|
||||||
|
|
@ -15,28 +35,35 @@ class CaptureFlag(tDef: CaptureFlagDefinition) extends Amenity {
|
||||||
private var carrier: Option[Player] = None
|
private var carrier: Option[Player] = None
|
||||||
|
|
||||||
def Target: Building = target
|
def Target: Building = target
|
||||||
def Target_=(new_target: Building): Building = {
|
def Target_=(newTarget: Building): Building = {
|
||||||
target = new_target
|
target = newTarget
|
||||||
target
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since a LLU belongs to a base, but needs to be picked up by the enemy faction we need to be able to override the faction that owns the LLU to the hacker faction
|
/**
|
||||||
|
* Since a LLU belongs to a base,
|
||||||
|
* but needs to be picked up by the enemy faction,
|
||||||
|
* override the faction that owns the LLU to display the hacker faction.
|
||||||
|
*/
|
||||||
override def Faction: PlanetSideEmpire.Value = faction
|
override def Faction: PlanetSideEmpire.Value = faction
|
||||||
override def Faction_=(new_faction: PlanetSideEmpire.Value): PlanetSideEmpire.Value = {
|
override def Faction_=(newFaction: PlanetSideEmpire.Value): PlanetSideEmpire.Value = {
|
||||||
faction = new_faction
|
faction = newFaction
|
||||||
faction
|
faction
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the flag is carried by a player, the position returned should be that of the carrier not the flag
|
/**
|
||||||
|
* When the flag is carried by a player, the position returned should be that of the carrier not the flag.
|
||||||
|
* @return the position of the carrier, if there is a player carrying the flag, or the flag itself
|
||||||
|
*/
|
||||||
override def Position: Vector3 = if (Carrier.nonEmpty) {
|
override def Position: Vector3 = if (Carrier.nonEmpty) {
|
||||||
carrier.get.Position
|
carrier.get.Position
|
||||||
} else {
|
} else {
|
||||||
Entity.Position
|
super.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
def Carrier: Option[Player] = carrier
|
def Carrier: Option[Player] = carrier
|
||||||
def Carrier_=(new_carrier: Option[Player]) : Option[Player] = {
|
def Carrier_=(newCarrier: Option[Player]) : Option[Player] = {
|
||||||
carrier = new_carrier
|
carrier = newCarrier
|
||||||
carrier
|
carrier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +80,6 @@ object CaptureFlag {
|
||||||
obj.Target = target
|
obj.Target = target
|
||||||
obj.Owner = owner
|
obj.Owner = owner
|
||||||
obj.Faction = faction
|
obj.Faction = faction
|
||||||
|
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import net.psforever.types.Vector3
|
||||||
*/
|
*/
|
||||||
class CaptureFlagSocket(tDef: CaptureFlagSocketDefinition)
|
class CaptureFlagSocket(tDef: CaptureFlagSocketDefinition)
|
||||||
extends Amenity {
|
extends Amenity {
|
||||||
|
private var lastFlag: Option[CaptureFlag] = None
|
||||||
private var spawnedCaptureFlag: Option[CaptureFlag] = None
|
private var spawnedCaptureFlag: Option[CaptureFlag] = None
|
||||||
|
|
||||||
def captureFlag: Option[CaptureFlag] = spawnedCaptureFlag
|
def captureFlag: Option[CaptureFlag] = spawnedCaptureFlag
|
||||||
|
|
@ -20,10 +21,19 @@ class CaptureFlagSocket(tDef: CaptureFlagSocketDefinition)
|
||||||
def captureFlag_=(flag: CaptureFlag): Option[CaptureFlag] = captureFlag_=(Some(flag))
|
def captureFlag_=(flag: CaptureFlag): Option[CaptureFlag] = captureFlag_=(Some(flag))
|
||||||
|
|
||||||
def captureFlag_=(flag: Option[CaptureFlag]): Option[CaptureFlag] = {
|
def captureFlag_=(flag: Option[CaptureFlag]): Option[CaptureFlag] = {
|
||||||
|
lastFlag = flag.orElse(lastFlag)
|
||||||
spawnedCaptureFlag = flag
|
spawnedCaptureFlag = flag
|
||||||
captureFlag
|
captureFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def previousFlag: Option[CaptureFlag] = lastFlag
|
||||||
|
|
||||||
|
def clearOldFlagData(): Unit = {
|
||||||
|
if (spawnedCaptureFlag.isEmpty) {
|
||||||
|
lastFlag = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def Definition : CaptureFlagSocketDefinition = tDef
|
def Definition : CaptureFlagSocketDefinition = tDef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ package net.psforever.objects.serverobject.pad.process
|
||||||
|
|
||||||
import akka.actor.Props
|
import akka.actor.Props
|
||||||
import net.psforever.objects.PlanetSideGameObject
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.etc.{ExplodingEntityReason, VehicleSpawnReason}
|
import net.psforever.objects.vital.etc.{ExplodingEntityReason, VehicleSpawnReason}
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.objects.vital.prop.DamageProperties
|
import net.psforever.objects.vital.prop.DamageProperties
|
||||||
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
package net.psforever.objects.serverobject.painbox
|
package net.psforever.objects.serverobject.painbox
|
||||||
|
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.serverobject.doors.Door
|
import net.psforever.objects.serverobject.doors.Door
|
||||||
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.etc.PainboxReason
|
import net.psforever.objects.vital.etc.PainboxReason
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.{Default, GlobalDefinitions, Player}
|
import net.psforever.objects.{Default, GlobalDefinitions, Player}
|
||||||
|
import net.psforever.services.Service
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
@ -58,7 +59,7 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
var commonBehavior: Receive = {
|
var commonBehavior: Receive = {
|
||||||
case "startup" =>
|
case Service.Startup() =>
|
||||||
if (!disabled && domain.midpoint == Vector3.Zero) {
|
if (!disabled && domain.midpoint == Vector3.Zero) {
|
||||||
initialStartup()
|
initialStartup()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import net.psforever.actors.zone.BuildingActor
|
||||||
import net.psforever.objects.{Default, NtuContainer, NtuStorageBehavior}
|
import net.psforever.objects.{Default, NtuContainer, NtuStorageBehavior}
|
||||||
import net.psforever.objects.serverobject.damage.Damageable
|
import net.psforever.objects.serverobject.damage.Damageable
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, AutoRepairStats, Building}
|
import net.psforever.objects.serverobject.structures.{Amenity, AutoRepairStats, Building}
|
||||||
|
import net.psforever.objects.vital.RepairFromAmenityAutoRepair
|
||||||
import net.psforever.util.Config
|
import net.psforever.util.Config
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -96,6 +97,7 @@ trait AmenityAutoRepair
|
||||||
wholeRepairAmount + wholeOverflow
|
wholeRepairAmount + wholeOverflow
|
||||||
}
|
}
|
||||||
PerformRepairs(obj, finalRepairAmount)
|
PerformRepairs(obj, finalRepairAmount)
|
||||||
|
obj.LogActivity(RepairFromAmenityAutoRepair(finalRepairAmount))
|
||||||
val currentTime = System.currentTimeMillis()
|
val currentTime = System.currentTimeMillis()
|
||||||
val taskTime = currentTime - autoRepairQueueTask.getOrElse(currentTime)
|
val taskTime = currentTime - autoRepairQueueTask.getOrElse(currentTime)
|
||||||
autoRepairQueueTask = Some(0L)
|
autoRepairQueueTask = Some(0L)
|
||||||
|
|
@ -148,7 +150,8 @@ trait AmenityAutoRepair
|
||||||
* or if the current process has stalled.
|
* or if the current process has stalled.
|
||||||
*/
|
*/
|
||||||
private def startAutoRepairIfStopped(): Unit = {
|
private def startAutoRepairIfStopped(): Unit = {
|
||||||
if(autoRepairQueueTask.isEmpty || stallDetection(stallTime = 15000L)) {
|
val stallTime: Long = 15000L
|
||||||
|
if(autoRepairQueueTask.isEmpty || stallDetection(stallTime)) {
|
||||||
trySetupAutoRepairInitial()
|
trySetupAutoRepairInitial()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package net.psforever.objects.serverobject.repair
|
||||||
|
|
||||||
import net.psforever.objects.Tool
|
import net.psforever.objects.Tool
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
|
import net.psforever.objects.sourcing.{SourceEntry, SourceWithHealthEntry}
|
||||||
|
import net.psforever.objects.vital.{DamagingActivity, RepairFromEquipment, SpawningActivity}
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,7 +25,7 @@ trait RepairableAmenity extends RepairableEntity {
|
||||||
object RepairableAmenity {
|
object RepairableAmenity {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resotred `Amenity` target dispatches two messages to chance its model and operational states.
|
* A restored `Amenity` target dispatches two messages to chance its model and operational states.
|
||||||
* These `PlanetSideAttributeMessage` attributes are the same as reported during zone load client configuration.
|
* These `PlanetSideAttributeMessage` attributes are the same as reported during zone load client configuration.
|
||||||
* @see `AvatarAction.PlanetsideAttributeToAll`
|
* @see `AvatarAction.PlanetsideAttributeToAll`
|
||||||
* @see `AvatarServiceMessage`
|
* @see `AvatarServiceMessage`
|
||||||
|
|
@ -37,5 +39,27 @@ object RepairableAmenity {
|
||||||
val targetGUID = target.GUID
|
val targetGUID = target.GUID
|
||||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 50, 0))
|
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 50, 0))
|
||||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 51, 0))
|
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 51, 0))
|
||||||
|
RestorationOfHistory(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The vitality change history will be forgotten as this entity, once destroyed, has been rebuilt.
|
||||||
|
* For the purpose of inheritance of experience due to interaction,
|
||||||
|
* the users who made an effort to repair the entity in its resurgence.
|
||||||
|
* @param target na
|
||||||
|
*/
|
||||||
|
def RestorationOfHistory(target: Repairable.Target): Unit = {
|
||||||
|
val list = target.ClearHistory()
|
||||||
|
val effort = list.slice(
|
||||||
|
list.lastIndexWhere {
|
||||||
|
case dam: DamagingActivity => dam.data.targetAfter.asInstanceOf[SourceWithHealthEntry].health == 0
|
||||||
|
case _ => false
|
||||||
|
},
|
||||||
|
list.size
|
||||||
|
).collect {
|
||||||
|
case entry: RepairFromEquipment => Some(entry.user)
|
||||||
|
case _ => None
|
||||||
|
}.flatten.distinctBy(_.Name)
|
||||||
|
target.LogActivity(SpawningActivity(SourceEntry(target), target.Zone.Number, effort.headOption))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
//Copyright (c) 2020 PSForever
|
//Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.serverobject.repair
|
package net.psforever.objects.serverobject.repair
|
||||||
|
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
|
import net.psforever.objects.vital.RepairFromEquipment
|
||||||
import net.psforever.objects.{Player, Tool}
|
import net.psforever.objects.{Player, Tool}
|
||||||
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
|
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
@ -92,6 +94,13 @@ trait RepairableEntity extends Repairable {
|
||||||
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)
|
InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
target.LogActivity(
|
||||||
|
RepairFromEquipment(
|
||||||
|
PlayerSource(player),
|
||||||
|
item.Definition,
|
||||||
|
repairValue
|
||||||
|
)
|
||||||
|
)
|
||||||
PerformRepairs(target, repairValue)
|
PerformRepairs(target, repairValue)
|
||||||
} else {
|
} else {
|
||||||
originalHealth
|
originalHealth
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ trait RepairableWeaponTurret extends RepairableEntity {
|
||||||
|
|
||||||
override def Restoration(target: Repairable.Target): Unit = {
|
override def Restoration(target: Repairable.Target): Unit = {
|
||||||
super.Restoration(target)
|
super.Restoration(target)
|
||||||
|
RepairableAmenity.RestorationOfHistory(target)
|
||||||
RepairableWeaponTurret.Restoration(RepairableObject)
|
RepairableWeaponTurret.Restoration(RepairableObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.structures
|
package net.psforever.objects.serverobject.structures
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
import akka.actor.ActorContext
|
import akka.actor.ActorContext
|
||||||
import net.psforever.actors.zone.BuildingActor
|
import net.psforever.actors.zone.BuildingActor
|
||||||
import net.psforever.objects.{GlobalDefinitions, NtuContainer, Player}
|
import net.psforever.objects.{GlobalDefinitions, NtuContainer, Player}
|
||||||
|
|
@ -20,32 +18,37 @@ import akka.actor.typed.scaladsl.adapter._
|
||||||
import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket}
|
import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket}
|
||||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
|
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class Building(
|
class Building(
|
||||||
private val name: String,
|
private val name: String,
|
||||||
private val building_guid: Int,
|
private val building_guid: Int,
|
||||||
private val map_id: Int,
|
private val map_id: Int,
|
||||||
private val zone: Zone,
|
private val zone: Zone,
|
||||||
private val buildingType: StructureType,
|
private val buildingType: StructureType,
|
||||||
private val buildingDefinition: BuildingDefinition
|
private val buildingDefinition: BuildingDefinition
|
||||||
) extends AmenityOwner
|
) extends AmenityOwner
|
||||||
with BlockMapEntity {
|
with BlockMapEntity {
|
||||||
|
|
||||||
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||||
private var playersInSOI: List[Player] = List.empty
|
private var playersInSOI: List[Player] = List.empty
|
||||||
private val capitols = List("Thoth", "Voltan", "Neit", "Anguta", "Eisa", "Verica")
|
private val capitols = List("Thoth", "Voltan", "Neit", "Anguta", "Eisa", "Verica")
|
||||||
private var forceDomeActive: Boolean = false
|
private var forceDomeActive: Boolean = false
|
||||||
|
private var participationFunc: Building.ParticipationLogic = Building.NoParticipation
|
||||||
super.Zone_=(zone)
|
super.Zone_=(zone)
|
||||||
super.GUID_=(PlanetSideGUID(building_guid)) //set
|
super.GUID_=(PlanetSideGUID(building_guid)) //set
|
||||||
Invalidate() //unset; guid can be used during setup, but does not stop being registered properly later
|
Invalidate() //unset; guid can be used during setup, but does not stop being registered properly later
|
||||||
|
|
||||||
override def toString = name
|
override def toString: String = name
|
||||||
|
|
||||||
def Name: String = name
|
def Name: String = name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The map_id is the identifier number used in BuildingInfoUpdateMessage. This is the index that the building appears in the MPO file starting from index 1
|
* The map_id is the identifier number used in BuildingInfoUpdateMessage. This is the index that the building appears in the MPO file starting from index 1
|
||||||
* The GUID is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage.
|
* The GUID is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage.
|
||||||
*/
|
*/
|
||||||
def MapId: Int = map_id
|
def MapId: Int = map_id
|
||||||
|
|
||||||
def IsCapitol: Boolean = capitols.contains(name)
|
def IsCapitol: Boolean = capitols.contains(name)
|
||||||
|
|
@ -82,10 +85,13 @@ class Building(
|
||||||
box.Actor ! Painbox.Stop()
|
box.Actor ! Painbox.Stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
participationFunc.Players(building = this, list)
|
||||||
playersInSOI = list
|
playersInSOI = list
|
||||||
playersInSOI
|
playersInSOI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def PlayerContribution: Map[Player, Long] = participationFunc.Contribution()
|
||||||
|
|
||||||
// Get all lattice neighbours
|
// Get all lattice neighbours
|
||||||
def AllNeighbours: Option[Set[Building]] = {
|
def AllNeighbours: Option[Set[Building]] = {
|
||||||
zone.Lattice find this match {
|
zone.Lattice find this match {
|
||||||
|
|
@ -139,7 +145,11 @@ class Building(
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
|
||||||
def GetFlagSocket: Option[CaptureFlagSocket] = this.Amenities.find(_.Definition == GlobalDefinitions.llm_socket).asInstanceOf[Option[CaptureFlagSocket]]
|
def GetFlagSocket: Option[CaptureFlagSocket] = {
|
||||||
|
this.Amenities
|
||||||
|
.find(_.Definition == GlobalDefinitions.llm_socket)
|
||||||
|
.map(_.asInstanceOf[CaptureFlagSocket])
|
||||||
|
}
|
||||||
def GetFlag: Option[CaptureFlag] = {
|
def GetFlag: Option[CaptureFlag] = {
|
||||||
GetFlagSocket match {
|
GetFlagSocket match {
|
||||||
case Some(socket) => socket.captureFlag
|
case Some(socket) => socket.captureFlag
|
||||||
|
|
@ -175,10 +185,10 @@ class Building(
|
||||||
val (hacking, hackingFaction, hackTime): (Boolean, PlanetSideEmpire.Value, Long) = CaptureTerminal match {
|
val (hacking, hackingFaction, hackTime): (Boolean, PlanetSideEmpire.Value, Long) = CaptureTerminal match {
|
||||||
case Some(obj: CaptureTerminal with Hackable) =>
|
case Some(obj: CaptureTerminal with Hackable) =>
|
||||||
obj.HackedBy match {
|
obj.HackedBy match {
|
||||||
case Some(Hackable.HackInfo(_, _, hfaction, _, start, length)) =>
|
case Some(Hackable.HackInfo(p, _, start, length)) =>
|
||||||
val hack_time_remaining_ms =
|
val hack_time_remaining_ms =
|
||||||
TimeUnit.MILLISECONDS.convert(math.max(0, start + length - System.nanoTime), TimeUnit.NANOSECONDS)
|
TimeUnit.MILLISECONDS.convert(math.max(0, start + length - System.nanoTime), TimeUnit.NANOSECONDS)
|
||||||
(true, hfaction, hack_time_remaining_ms)
|
(true, p.Faction, hack_time_remaining_ms)
|
||||||
case _ =>
|
case _ =>
|
||||||
(false, PlanetSideEmpire.NEUTRAL, 0L)
|
(false, PlanetSideEmpire.NEUTRAL, 0L)
|
||||||
}
|
}
|
||||||
|
|
@ -199,8 +209,8 @@ class Building(
|
||||||
}
|
}
|
||||||
val cavernBenefit: Set[CavernBenefit] = if (
|
val cavernBenefit: Set[CavernBenefit] = if (
|
||||||
generatorState != PlanetSideGeneratorState.Destroyed &&
|
generatorState != PlanetSideGeneratorState.Destroyed &&
|
||||||
faction != PlanetSideEmpire.NEUTRAL &&
|
faction != PlanetSideEmpire.NEUTRAL &&
|
||||||
connectedCavern().nonEmpty
|
connectedCavern().nonEmpty
|
||||||
) {
|
) {
|
||||||
Set(CavernBenefit.VehicleModule, CavernBenefit.EquipmentModule)
|
Set(CavernBenefit.VehicleModule, CavernBenefit.EquipmentModule)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -233,22 +243,28 @@ class Building(
|
||||||
}
|
}
|
||||||
|
|
||||||
def hasLatticeBenefit(wantedBenefit: LatticeBenefit): Boolean = {
|
def hasLatticeBenefit(wantedBenefit: LatticeBenefit): Boolean = {
|
||||||
val genState = Generator match {
|
val baseDownState = (NtuSource match {
|
||||||
case Some(obj) => obj.Condition != PlanetSideGeneratorState.Destroyed
|
case Some(ntu) => ntu.NtuCapacitor < 1f
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}) ||
|
||||||
if (genState || Faction == PlanetSideEmpire.NEUTRAL) {
|
(Generator match {
|
||||||
|
case Some(obj) => obj.Condition == PlanetSideGeneratorState.Destroyed
|
||||||
|
case _ => false
|
||||||
|
}) ||
|
||||||
|
Faction == PlanetSideEmpire.NEUTRAL
|
||||||
|
if (baseDownState) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
// Check this Building is on the lattice first
|
// Check this Building is on the lattice first
|
||||||
zone.Lattice find this match {
|
zone.Lattice find this match {
|
||||||
case Some(_) =>
|
case Some(_) =>
|
||||||
|
val faction = Faction
|
||||||
val subGraph = Zone.Lattice filter (
|
val subGraph = Zone.Lattice filter (
|
||||||
(b : Building) =>
|
(b: Building) =>
|
||||||
b.Faction == this.Faction &&
|
b.Faction == faction &&
|
||||||
!b.CaptureTerminalIsHacked &&
|
!b.CaptureTerminalIsHacked &&
|
||||||
b.NtuLevel > 0 &&
|
b.NtuLevel > 0 &&
|
||||||
(b.Generator.isEmpty || b.Generator.get.Condition != PlanetSideGeneratorState.Destroyed)
|
(b.Generator.isEmpty || b.Generator.get.Condition != PlanetSideGeneratorState.Destroyed)
|
||||||
)
|
)
|
||||||
findLatticeBenefit(wantedBenefit, subGraph)
|
findLatticeBenefit(wantedBenefit, subGraph)
|
||||||
case None =>
|
case None =>
|
||||||
|
|
@ -282,7 +298,10 @@ class Building(
|
||||||
case Some(obj) => obj.Condition
|
case Some(obj) => obj.Condition
|
||||||
case _ => PlanetSideGeneratorState.Normal
|
case _ => PlanetSideGeneratorState.Normal
|
||||||
}
|
}
|
||||||
if (genState == PlanetSideGeneratorState.Destroyed || Faction == PlanetSideEmpire.NEUTRAL) {
|
if (genState == PlanetSideGeneratorState.Destroyed ||
|
||||||
|
Faction == PlanetSideEmpire.NEUTRAL ||
|
||||||
|
CaptureTerminalIsHacked
|
||||||
|
) {
|
||||||
Set(LatticeBenefit.None)
|
Set(LatticeBenefit.None)
|
||||||
} else {
|
} else {
|
||||||
friendlyFunctionalNeighborhood().map { _.Definition.LatticeLinkBenefit }
|
friendlyFunctionalNeighborhood().map { _.Definition.LatticeLinkBenefit }
|
||||||
|
|
@ -296,10 +315,10 @@ class Building(
|
||||||
while (currBuilding.nonEmpty) {
|
while (currBuilding.nonEmpty) {
|
||||||
val building = currBuilding.head
|
val building = currBuilding.head
|
||||||
val neighborsToAdd = if (!visitedNeighbors.contains(building.MapId)
|
val neighborsToAdd = if (!visitedNeighbors.contains(building.MapId)
|
||||||
&& (building match { case _ : WarpGate => false; case _ => true })
|
&& (building match { case _ : WarpGate => false; case _ => true })
|
||||||
&& !building.CaptureTerminalIsHacked
|
&& !building.CaptureTerminalIsHacked
|
||||||
&& building.NtuLevel > 0
|
&& building.NtuLevel > 0
|
||||||
&& (building.Generator match {
|
&& (building.Generator match {
|
||||||
case Some(o) => o.Condition != PlanetSideGeneratorState.Destroyed
|
case Some(o) => o.Condition != PlanetSideGeneratorState.Destroyed
|
||||||
case _ => true
|
case _ => true
|
||||||
})
|
})
|
||||||
|
|
@ -321,14 +340,14 @@ class Building(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starting from an overworld zone facility,
|
* Starting from an overworld zone facility,
|
||||||
* find a lattice connected cavern facility that is the same faction as this starting building.
|
* find a lattice connected cavern facility that is the same faction as this starting building.
|
||||||
* Except for the necessary examination of the major facility on the other side of a warp gate pair,
|
* Except for the necessary examination of the major facility on the other side of a warp gate pair,
|
||||||
* do not let the search escape the current zone into another.
|
* do not let the search escape the current zone into another.
|
||||||
* If we start in a cavern zone, do not continue a fruitless search;
|
* If we start in a cavern zone, do not continue a fruitless search;
|
||||||
* just fail.
|
* just fail.
|
||||||
* @return the discovered faction-aligned cavern facility
|
* @return the discovered faction-aligned cavern facility
|
||||||
*/
|
*/
|
||||||
def connectedCavern(): Option[Building] = net.psforever.objects.zones.Zone.findConnectedCavernFacility(building = this)
|
def connectedCavern(): Option[Building] = net.psforever.objects.zones.Zone.findConnectedCavernFacility(building = this)
|
||||||
|
|
||||||
def BuildingType: StructureType = buildingType
|
def BuildingType: StructureType = buildingType
|
||||||
|
|
@ -339,10 +358,46 @@ class Building(
|
||||||
|
|
||||||
override def Continent_=(zone: String): String = Continent //building never leaves zone after being set in constructor
|
override def Continent_=(zone: String): String = Continent //building never leaves zone after being set in constructor
|
||||||
|
|
||||||
|
override def Amenities_=(obj: Amenity): List[Amenity] = {
|
||||||
|
obj match {
|
||||||
|
case _: CaptureTerminal => participationFunc = Building.FacilityHackParticipation
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
super.Amenities_=(obj)
|
||||||
|
}
|
||||||
|
|
||||||
def Definition: BuildingDefinition = buildingDefinition
|
def Definition: BuildingDefinition = buildingDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
object Building {
|
object Building {
|
||||||
|
trait ParticipationLogic {
|
||||||
|
def Players(building: Building, list: List[Player]): Unit = { }
|
||||||
|
def Contribution(): Map[Player, Long]
|
||||||
|
}
|
||||||
|
|
||||||
|
final case object NoParticipation extends ParticipationLogic {
|
||||||
|
def Contribution(): Map[Player, Long] = Map.empty[Player, Long]
|
||||||
|
}
|
||||||
|
|
||||||
|
final case object FacilityHackParticipation extends ParticipationLogic {
|
||||||
|
private var playerContribution: mutable.HashMap[Player, Long] = mutable.HashMap[Player, Long]()
|
||||||
|
|
||||||
|
override def Players(building: Building, list: List[Player]): Unit = {
|
||||||
|
if (list.isEmpty) {
|
||||||
|
playerContribution.clear()
|
||||||
|
} else {
|
||||||
|
val hackTime = (building.CaptureTerminal.get.Definition.FacilityHackTime + 10.minutes).toMillis
|
||||||
|
val curr = System.currentTimeMillis()
|
||||||
|
val list2 = list.map { p => (p, curr) }
|
||||||
|
playerContribution = playerContribution.filterNot { case (p, t) =>
|
||||||
|
list2.contains(p) || curr - t > hackTime
|
||||||
|
} ++ list2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def Contribution(): Map[Player, Long] = playerContribution.toMap
|
||||||
|
}
|
||||||
|
|
||||||
final val NoBuilding: Building =
|
final val NoBuilding: Building =
|
||||||
new Building(name = "", 0, map_id = 0, Zone.Nowhere, StructureType.Platform, GlobalDefinitions.building) {
|
new Building(name = "", 0, map_id = 0, Zone.Nowhere, StructureType.Platform, GlobalDefinitions.building) {
|
||||||
override def Faction_=(faction: PlanetSideEmpire.Value): PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
override def Faction_=(faction: PlanetSideEmpire.Value): PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||||
|
|
@ -355,11 +410,11 @@ object Building {
|
||||||
}
|
}
|
||||||
|
|
||||||
def Structure(
|
def Structure(
|
||||||
buildingType: StructureType,
|
buildingType: StructureType,
|
||||||
location: Vector3,
|
location: Vector3,
|
||||||
rotation: Vector3,
|
rotation: Vector3,
|
||||||
definition: BuildingDefinition
|
definition: BuildingDefinition
|
||||||
)(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): Building = {
|
)(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): Building = {
|
||||||
val obj = new Building(name, guid, map_id, zone, buildingType, definition)
|
val obj = new Building(name, guid, map_id, zone, buildingType, definition)
|
||||||
obj.Position = location
|
obj.Position = location
|
||||||
obj.Orientation = rotation
|
obj.Orientation = rotation
|
||||||
|
|
@ -368,9 +423,9 @@ object Building {
|
||||||
}
|
}
|
||||||
|
|
||||||
def Structure(
|
def Structure(
|
||||||
buildingType: StructureType,
|
buildingType: StructureType,
|
||||||
location: Vector3
|
location: Vector3
|
||||||
)(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): Building = {
|
)(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): Building = {
|
||||||
val obj = new Building(name, guid, map_id, zone, buildingType, GlobalDefinitions.building)
|
val obj = new Building(name, guid, map_id, zone, buildingType, GlobalDefinitions.building)
|
||||||
obj.Position = location
|
obj.Position = location
|
||||||
obj.Actor = context.spawn(BuildingActor(zone, obj), s"$map_id-$buildingType-building").toClassic
|
obj.Actor = context.spawn(BuildingActor(zone, obj), s"$map_id-$buildingType-building").toClassic
|
||||||
|
|
@ -378,18 +433,18 @@ object Building {
|
||||||
}
|
}
|
||||||
|
|
||||||
def Structure(
|
def Structure(
|
||||||
buildingType: StructureType
|
buildingType: StructureType
|
||||||
)(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): Building = {
|
)(name: String, guid: Int, map_id: Int, zone: Zone, context: ActorContext): Building = {
|
||||||
val obj = new Building(name, guid, map_id, zone, buildingType, GlobalDefinitions.building)
|
val obj = new Building(name, guid, map_id, zone, buildingType, GlobalDefinitions.building)
|
||||||
obj.Actor = context.spawn(BuildingActor(zone, obj), s"$map_id-$buildingType-building").toClassic
|
obj.Actor = context.spawn(BuildingActor(zone, obj), s"$map_id-$buildingType-building").toClassic
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
def Structure(
|
def Structure(
|
||||||
buildingType: StructureType,
|
buildingType: StructureType,
|
||||||
buildingDefinition: BuildingDefinition,
|
buildingDefinition: BuildingDefinition,
|
||||||
location: Vector3
|
location: Vector3
|
||||||
)(name: String, guid: Int, id: Int, zone: Zone, context: ActorContext): Building = {
|
)(name: String, guid: Int, id: Int, zone: Zone, context: ActorContext): Building = {
|
||||||
val obj = new Building(name, guid, id, zone, buildingType, buildingDefinition)
|
val obj = new Building(name, guid, id, zone, buildingType, buildingDefinition)
|
||||||
obj.Position = location
|
obj.Position = location
|
||||||
obj.Actor = context.spawn(BuildingActor(zone, obj), s"$id-$buildingType-building").toClassic
|
obj.Actor = context.spawn(BuildingActor(zone, obj), s"$id-$buildingType-building").toClassic
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
// Copyright (c) 2020 PSForever
|
|
||||||
package net.psforever.objects.serverobject.terminals
|
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
|
||||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
|
|
||||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
|
||||||
|
|
||||||
import scala.util.{Failure, Success}
|
|
||||||
|
|
||||||
object CaptureTerminals {
|
|
||||||
private val log = org.log4s.getLogger("CaptureTerminals")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The process of hacking an object is completed.
|
|
||||||
* Pass the message onto the hackable object and onto the local events system.
|
|
||||||
* @param target the `Hackable` object that has been hacked
|
|
||||||
* @param unk na;
|
|
||||||
* used by `HackMessage` as `unk5`
|
|
||||||
* @see `HackMessage`
|
|
||||||
*/
|
|
||||||
//TODO add params here depending on which params in HackMessage are important
|
|
||||||
def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Long)(): Unit = {
|
|
||||||
import akka.pattern.ask
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}")
|
|
||||||
// Wait for the target actor to set the HackedBy property
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
ask(target.Actor, CommonMessages.Hack(hackingPlayer, target))(1 second).mapTo[Boolean].onComplete {
|
|
||||||
case Success(_) =>
|
|
||||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
|
||||||
target.Zone.id,
|
|
||||||
LocalAction.TriggerSound(hackingPlayer.GUID, target.HackSound, hackingPlayer.Position, 30, 0.49803925f)
|
|
||||||
)
|
|
||||||
val isResecured = hackingPlayer.Faction == target.Faction
|
|
||||||
if (isResecured) {
|
|
||||||
// Resecure the CC
|
|
||||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
|
||||||
target.Zone.id,
|
|
||||||
LocalAction.ResecureCaptureTerminal(
|
|
||||||
target
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Start the CC hack timer
|
|
||||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
|
||||||
target.Zone.id,
|
|
||||||
LocalAction.StartCaptureTerminalHack(
|
|
||||||
target
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,8 +2,13 @@
|
||||||
package net.psforever.objects.serverobject.terminals
|
package net.psforever.objects.serverobject.terminals
|
||||||
|
|
||||||
import akka.actor.{ActorRef, Cancellable}
|
import akka.actor.{ActorRef, Cancellable}
|
||||||
|
import net.psforever.objects.sourcing.AmenitySource
|
||||||
|
import org.log4s.Logger
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
//
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, VehicleSource}
|
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||||
|
|
@ -12,39 +17,37 @@ import net.psforever.objects.serverobject.damage.DamageableAmenity
|
||||||
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
|
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
|
||||||
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity}
|
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity}
|
||||||
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
||||||
import net.psforever.objects.vital.{HealFromTerm, RepairFromTerm}
|
import net.psforever.objects.vital.{HealFromTerm, RepairFromTerm, Vitality}
|
||||||
|
import net.psforever.objects.zones.ZoneAware
|
||||||
import net.psforever.packet.game.InventoryStateMessage
|
import net.psforever.packet.game.InventoryStateMessage
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
|
|
||||||
import scala.collection.mutable
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An `Actor` that handles messages being dispatched to a specific `ProximityTerminal`.
|
* An `Actor` that handles messages being dispatched to a specific `ProximityTerminal`.
|
||||||
* Although this "terminal" itself does not accept the same messages as a normal `Terminal` object,
|
* Although this "terminal" itself does not accept the same messages as a normal `Terminal` object,
|
||||||
* it returns the same type of messages - wrapped in a `TerminalMessage` - to the `sender`.
|
* it returns the same type of messages - wrapped in a `TerminalMessage` - to the `sender`.
|
||||||
* @param term the proximity unit (terminal)
|
* @param term the proximity unit (terminal)
|
||||||
*/
|
*/
|
||||||
class ProximityTerminalControl(term: Terminal with ProximityUnit)
|
class ProximityTerminalControl(term: Terminal with ProximityUnit)
|
||||||
extends PoweredAmenityControl
|
extends PoweredAmenityControl
|
||||||
with FactionAffinityBehavior.Check
|
with FactionAffinityBehavior.Check
|
||||||
with HackableBehavior.GenericHackable
|
with HackableBehavior.GenericHackable
|
||||||
with DamageableAmenity
|
with DamageableAmenity
|
||||||
with RepairableAmenity
|
with RepairableAmenity
|
||||||
with AmenityAutoRepair {
|
with AmenityAutoRepair {
|
||||||
def FactionObject = term
|
def FactionObject: Terminal with ProximityUnit = term
|
||||||
def HackableObject = term
|
def HackableObject: Terminal with ProximityUnit = term
|
||||||
def TerminalObject = term
|
def TerminalObject: Terminal with ProximityUnit = term
|
||||||
def DamageableObject = term
|
def DamageableObject: Terminal with ProximityUnit = term
|
||||||
def RepairableObject = term
|
def RepairableObject: Terminal with ProximityUnit = term
|
||||||
def AutoRepairObject = term
|
def AutoRepairObject: Terminal with ProximityUnit = term
|
||||||
|
|
||||||
var terminalAction: Cancellable = Default.Cancellable
|
var terminalAction: Cancellable = Default.Cancellable
|
||||||
val callbacks: mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]()
|
val callbacks: mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]()
|
||||||
val log = org.log4s.getLogger
|
val log: Logger = org.log4s.getLogger
|
||||||
|
|
||||||
val commonBehavior: Receive = checkBehavior
|
val commonBehavior: Receive = checkBehavior
|
||||||
.orElse(takesDamage)
|
.orElse(takesDamage)
|
||||||
|
|
@ -63,7 +66,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
|
||||||
.orElse(hackableBehavior)
|
.orElse(hackableBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
case CommonMessages.Use(player, Some(item: SimpleItem))
|
case CommonMessages.Use(player, Some(item: SimpleItem))
|
||||||
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
|
||||||
//TODO setup certifications check
|
//TODO setup certifications check
|
||||||
term.Owner match {
|
term.Owner match {
|
||||||
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && term.HackedBy.isEmpty =>
|
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && term.HackedBy.isEmpty =>
|
||||||
|
|
@ -214,16 +217,16 @@ object ProximityTerminalControl {
|
||||||
private case class TerminalAction()
|
private case class TerminalAction()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine which functionality to pursue by a generic proximity-functional unit given the target for its activity.
|
* Determine which functionality to pursue by a generic proximity-functional unit given the target for its activity.
|
||||||
* @see `VehicleService:receive, ProximityUnit.Action`
|
* @see `VehicleService:receive, ProximityUnit.Action`
|
||||||
* @param terminal the proximity-based unit
|
* @param terminal the proximity-based unit
|
||||||
* @param target the object being affected by the unit
|
* @param target the object being affected by the unit
|
||||||
*/
|
*/
|
||||||
def selectAndTryProximityUnitBehavior(
|
def selectAndTryProximityUnitBehavior(
|
||||||
callback: ActorRef,
|
callback: ActorRef,
|
||||||
terminal: Terminal with ProximityUnit,
|
terminal: Terminal with ProximityUnit,
|
||||||
target: PlanetSideGameObject
|
target: PlanetSideGameObject
|
||||||
): Boolean = {
|
): Boolean = {
|
||||||
(terminal.Definition, target) match {
|
(terminal.Definition, target) match {
|
||||||
case (_: MedicalTerminalDefinition, p: Player) => HealthAndArmorTerminal(terminal, p)
|
case (_: MedicalTerminalDefinition, p: Player) => HealthAndArmorTerminal(terminal, p)
|
||||||
case (_: WeaponRechargeTerminalDefinition, p: Player) => WeaponRechargeTerminal(terminal, p)
|
case (_: WeaponRechargeTerminalDefinition, p: Player) => WeaponRechargeTerminal(terminal, p)
|
||||||
|
|
@ -234,110 +237,131 @@ object ProximityTerminalControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When standing on the platform of a(n advanced) medical terminal,
|
* When standing on the platform of a(n advanced) medical terminal,
|
||||||
* restore the player's health and armor points (when they need their health and armor points restored).
|
* restore the player's health and armor points (when they need their health and armor points restored).
|
||||||
* If the player is both fully healed and fully repaired, stop using the terminal.
|
* If the player is both fully healed and fully repaired, stop using the terminal.
|
||||||
* @param unit the medical terminal
|
* @param unit the medical terminal
|
||||||
* @param target the player being healed
|
* @param target the player being healed
|
||||||
*/
|
*/
|
||||||
def HealthAndArmorTerminal(unit: Terminal with ProximityUnit, target: Player): Boolean = {
|
def HealthAndArmorTerminal(unit: Terminal with ProximityUnit, target: Player): Boolean = {
|
||||||
val medDef = unit.Definition.asInstanceOf[MedicalTerminalDefinition]
|
val medDef = unit.Definition.asInstanceOf[MedicalTerminalDefinition]
|
||||||
val healAmount = medDef.HealAmount
|
val fullHeal = HealAction(unit, target, medDef.HealAmount, PlayerHealthCallback)
|
||||||
val healthFull: Boolean = if (healAmount != 0 && target.Health < target.MaxHealth) {
|
val fullRepair = ArmorRepairAction(unit, target, medDef.ArmorAmount)
|
||||||
target.History(HealFromTerm(PlayerSource(target), healAmount, 0, medDef))
|
fullHeal && fullRepair
|
||||||
HealAction(target, healAmount)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
val repairAmount = medDef.ArmorAmount
|
|
||||||
val armorFull: Boolean = if (repairAmount != 0 && target.Armor < target.MaxArmor) {
|
|
||||||
target.History(HealFromTerm(PlayerSource(target), 0, repairAmount, medDef))
|
|
||||||
ArmorRepairAction(target, repairAmount)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
healthFull && armorFull
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore, at most, a specific amount of health points on a player.
|
* When driving a vehicle close to a rearm/repair silo,
|
||||||
* Send messages to connected client and to events system.
|
* restore the vehicle's health points.
|
||||||
* @param tplayer the player
|
* If the vehicle is fully repaired, stop using the terminal.
|
||||||
* @param healValue the amount to heal;
|
* @param unit the terminal
|
||||||
* 10 by default
|
* @param target the vehicle being repaired
|
||||||
* @return whether the player can be repaired for any more health points
|
*/
|
||||||
*/
|
|
||||||
def HealAction(tplayer: Player, healValue: Int = 10): Boolean = {
|
|
||||||
tplayer.Health = tplayer.Health + healValue
|
|
||||||
val zone = tplayer.Zone
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
zone.id,
|
|
||||||
AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 0, tplayer.Health)
|
|
||||||
)
|
|
||||||
tplayer.Health == tplayer.MaxHealth
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore, at most, a specific amount of personal armor points on a player.
|
|
||||||
* Send messages to connected client and to events system.
|
|
||||||
* @param tplayer the player
|
|
||||||
* @param repairValue the amount to repair;
|
|
||||||
* 10 by default
|
|
||||||
* @return whether the player can be repaired for any more armor points
|
|
||||||
*/
|
|
||||||
def ArmorRepairAction(tplayer: Player, repairValue: Int = 10): Boolean = {
|
|
||||||
tplayer.Armor = tplayer.Armor + repairValue
|
|
||||||
val zone = tplayer.Zone
|
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
|
||||||
zone.id,
|
|
||||||
AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 4, tplayer.Armor)
|
|
||||||
)
|
|
||||||
tplayer.Armor == tplayer.MaxArmor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When driving a vehicle close to a rearm/repair silo,
|
|
||||||
* restore the vehicle's health points.
|
|
||||||
* If the vehicle is fully repaired, stop using the terminal.
|
|
||||||
* @param unit the terminal
|
|
||||||
* @param target the vehicle being repaired
|
|
||||||
*/
|
|
||||||
def VehicleRepairTerminal(unit: Terminal with ProximityUnit, target: Vehicle): Boolean = {
|
def VehicleRepairTerminal(unit: Terminal with ProximityUnit, target: Vehicle): Boolean = {
|
||||||
val medDef = unit.Definition.asInstanceOf[MedicalTerminalDefinition]
|
unit.Definition match {
|
||||||
val healAmount = medDef.HealAmount
|
case medDef: MedicalTerminalDefinition if !target.Destroyed && unit.Validate(target) =>
|
||||||
val maxHealth = target.MaxHealth
|
HealAction(unit, target, medDef.HealAmount, VehicleHealthCallback)
|
||||||
val noMoreHeal = if (!target.Destroyed && unit.Validate(target)) {
|
case _ =>
|
||||||
//repair vehicle
|
|
||||||
if (healAmount > 0 && target.Health < maxHealth) {
|
|
||||||
target.Health = target.Health + healAmount
|
|
||||||
target.History(RepairFromTerm(VehicleSource(target), healAmount, medDef))
|
|
||||||
val zone = target.Zone
|
|
||||||
zone.VehicleEvents ! VehicleServiceMessage(
|
|
||||||
zone.id,
|
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 0, target.Health)
|
|
||||||
)
|
|
||||||
target.Health == maxHealth
|
|
||||||
} else {
|
|
||||||
true
|
true
|
||||||
}
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
noMoreHeal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When standing in a friendly SOI whose facility is under the influence of an Ancient Weapon Module benefit,
|
* Restore, at most, a specific amount of health points on a player.
|
||||||
* and the player is in possession of Ancient weaponnry whose magazine is not full,
|
* Send messages to connected client and to events system.
|
||||||
* restore some ammunition to its magazine.
|
* @param terminal na
|
||||||
* If no valid weapons are discovered or the discovered valid weapons have full magazines, stop using the terminal.
|
* @param target that which will accept the health
|
||||||
* @param unit the terminal
|
* @param healAmount health value to be given to the target
|
||||||
* @param target the player with weapons being recharged
|
* @param updateFunc callback to update the UI
|
||||||
*/
|
* @return whether the target can be healed any further
|
||||||
|
*/
|
||||||
|
def HealAction(
|
||||||
|
terminal: Terminal,
|
||||||
|
target: PlanetSideGameObject with Vitality with ZoneAware,
|
||||||
|
healAmount: Int,
|
||||||
|
updateFunc: PlanetSideGameObject with Vitality with ZoneAware=>Unit
|
||||||
|
): Boolean = {
|
||||||
|
val health = target.Health
|
||||||
|
val maxHealth = target.MaxHealth
|
||||||
|
val nextHealth = health + healAmount
|
||||||
|
if (healAmount != 0 && health < maxHealth) {
|
||||||
|
val finalHealthAmount = if (nextHealth > maxHealth) {
|
||||||
|
nextHealth - maxHealth
|
||||||
|
} else {
|
||||||
|
healAmount
|
||||||
|
}
|
||||||
|
target.Health = health + finalHealthAmount
|
||||||
|
target.LogActivity(HealFromTerm(AmenitySource(terminal), finalHealthAmount))
|
||||||
|
updateFunc(target)
|
||||||
|
target.Health == maxHealth
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def PlayerHealthCallback(target: PlanetSideGameObject with Vitality with ZoneAware): Unit = {
|
||||||
|
val zone = target.Zone
|
||||||
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
|
zone.id,
|
||||||
|
AvatarAction.PlanetsideAttributeToAll(target.GUID, 0, target.Health)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def VehicleHealthCallback(target: PlanetSideGameObject with Vitality with ZoneAware): Unit = {
|
||||||
|
val zone = target.Zone
|
||||||
|
zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
|
zone.id,
|
||||||
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 0, target.Health)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore, at most, a specific amount of personal armor points on a player.
|
||||||
|
* Send messages to connected client and to events system.
|
||||||
|
* @param terminal na
|
||||||
|
* @param target that which will accept the repair
|
||||||
|
* @param repairAmount armor value to be given to the target
|
||||||
|
* @return whether the target can be repaired any further
|
||||||
|
*/
|
||||||
|
def ArmorRepairAction(
|
||||||
|
terminal: Terminal,
|
||||||
|
target: Player,
|
||||||
|
repairAmount: Int
|
||||||
|
): Boolean = {
|
||||||
|
val armor = target.Armor
|
||||||
|
val maxArmor = target.MaxArmor
|
||||||
|
val nextArmor = armor + repairAmount
|
||||||
|
if (repairAmount != 0 && armor < maxArmor) {
|
||||||
|
val finalRepairAmount = if (nextArmor > maxArmor) {
|
||||||
|
nextArmor - maxArmor
|
||||||
|
} else {
|
||||||
|
repairAmount
|
||||||
|
}
|
||||||
|
target.Armor = armor + finalRepairAmount
|
||||||
|
target.LogActivity(RepairFromTerm(AmenitySource(terminal), finalRepairAmount))
|
||||||
|
val zone = target.Zone
|
||||||
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
|
zone.id,
|
||||||
|
AvatarAction.PlanetsideAttributeToAll(target.GUID, 4, target.Armor)
|
||||||
|
)
|
||||||
|
target.Armor == maxArmor
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When standing in a friendly SOI whose facility is under the influence of an Ancient Weapon Module benefit,
|
||||||
|
* and the player is in possession of Ancient weaponnry whose magazine is not full,
|
||||||
|
* restore some ammunition to its magazine.
|
||||||
|
* If no valid weapons are discovered or the discovered valid weapons have full magazines, stop using the terminal.
|
||||||
|
* @param unit the terminal
|
||||||
|
* @param target the player with weapons being recharged
|
||||||
|
*/
|
||||||
def WeaponRechargeTerminal(unit: Terminal with ProximityUnit, target: Player): Boolean = {
|
def WeaponRechargeTerminal(unit: Terminal with ProximityUnit, target: Player): Boolean = {
|
||||||
val result = WeaponsBeingRechargedWithSomeAmmunition(
|
val result = WeaponsBeingRechargedWithSomeAmmunition(
|
||||||
unit.Definition.asInstanceOf[WeaponRechargeTerminalDefinition].AmmoAmount,
|
unit.Definition.asInstanceOf[WeaponRechargeTerminalDefinition].AmmoAmount,
|
||||||
target.Holsters().map { _.Equipment }.flatten.toIterable ++ target.Inventory.Items.map { _.obj }
|
target.Holsters().flatMap { _.Equipment }.toIterable ++ target.Inventory.Items.map { _.obj }
|
||||||
)
|
)
|
||||||
val events = unit.Zone.AvatarEvents
|
val events = unit.Zone.AvatarEvents
|
||||||
val channel = target.Name
|
val channel = target.Name
|
||||||
|
|
@ -349,17 +373,17 @@ object ProximityTerminalControl {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
!result.unzip._2.flatten.exists { slot => slot.Magazine < slot.MaxMagazine() }
|
!result.flatMap { _._2 }.exists { slot => slot.Magazine < slot.MaxMagazine() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When driving close to a rearm/repair silo whose facility is under the influence of an Ancient Weapon Module benefit,
|
* When driving close to a rearm/repair silo whose facility is under the influence of an Ancient Weapon Module benefit,
|
||||||
* and the vehicle is an Ancient vehicle with mounted weaponry whose magazine(s) is not full,
|
* and the vehicle is an Ancient vehicle with mounted weaponry whose magazine(s) is not full,
|
||||||
* restore some ammunition to the magazine(s).
|
* restore some ammunition to the magazine(s).
|
||||||
* If no valid weapons are discovered or the discovered valid weapons have full magazines, stop using the terminal.
|
* If no valid weapons are discovered or the discovered valid weapons have full magazines, stop using the terminal.
|
||||||
* @param unit the terminal
|
* @param unit the terminal
|
||||||
* @param target the vehicle with weapons being recharged
|
* @param target the vehicle with weapons being recharged
|
||||||
*/
|
*/
|
||||||
def WeaponRechargeTerminal(unit: Terminal with ProximityUnit, target: Vehicle): Boolean = {
|
def WeaponRechargeTerminal(unit: Terminal with ProximityUnit, target: Vehicle): Boolean = {
|
||||||
val result = WeaponsBeingRechargedWithSomeAmmunition(
|
val result = WeaponsBeingRechargedWithSomeAmmunition(
|
||||||
unit.Definition.asInstanceOf[WeaponRechargeTerminalDefinition].AmmoAmount,
|
unit.Definition.asInstanceOf[WeaponRechargeTerminalDefinition].AmmoAmount,
|
||||||
|
|
@ -375,17 +399,17 @@ object ProximityTerminalControl {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
!result.unzip._2.flatten.exists { slot => slot.Magazine < slot.MaxMagazine() }
|
!result.flatMap { _._2 }.exists { slot => slot.Magazine < slot.MaxMagazine() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all weapons with magazines that need to have ammunition reloaded,
|
* Collect all weapons with magazines that need to have ammunition reloaded,
|
||||||
* and reload some ammunition into them.
|
* and reload some ammunition into them.
|
||||||
* @param ammoAdded the amount of ammo to be added to a weapon
|
* @param ammoAdded the amount of ammo to be added to a weapon
|
||||||
* @param equipment the equipment being considered;
|
* @param equipment the equipment being considered;
|
||||||
* weapons whose ammo will be increased will be isolated
|
* weapons whose ammo will be increased will be isolated
|
||||||
* @return na
|
* @return na
|
||||||
*/
|
*/
|
||||||
def WeaponsBeingRechargedWithSomeAmmunition(
|
def WeaponsBeingRechargedWithSomeAmmunition(
|
||||||
ammoAdded: Int,
|
ammoAdded: Int,
|
||||||
equipment: Iterable[Equipment]
|
equipment: Iterable[Equipment]
|
||||||
|
|
@ -399,12 +423,12 @@ object ProximityTerminalControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all magazines from this weapon that need to have ammunition reloaded,
|
* Collect all magazines from this weapon that need to have ammunition reloaded,
|
||||||
* and reload some ammunition into them.
|
* and reload some ammunition into them.
|
||||||
* @param ammoAdded the amount of ammo to be added to a weapon
|
* @param ammoAdded the amount of ammo to be added to a weapon
|
||||||
* @param slots the vehicle with weapons being recharged
|
* @param slots the vehicle with weapons being recharged
|
||||||
* @return ammunition slots that were affected
|
* @return ammunition slots that were affected
|
||||||
*/
|
*/
|
||||||
def WeaponAmmoRecharge(
|
def WeaponAmmoRecharge(
|
||||||
ammoAdded: Int,
|
ammoAdded: Int,
|
||||||
slots: List[Tool.FireModeSlot]
|
slots: List[Tool.FireModeSlot]
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
package net.psforever.objects.serverobject.terminals.capture
|
package net.psforever.objects.serverobject.terminals.capture
|
||||||
|
|
||||||
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
||||||
|
import scala.concurrent.duration.{Duration, FiniteDuration}
|
||||||
|
|
||||||
class CaptureTerminalDefinition(objectId: Int) extends AmenityDefinition(objectId) {
|
class CaptureTerminalDefinition(objectId: Int) extends AmenityDefinition(objectId) {
|
||||||
Name = objectId match {
|
private var hackTime: FiniteDuration = Duration.Zero
|
||||||
case 158 => "capture_terminal"
|
|
||||||
case 751 => "secondary_capture"
|
def FacilityHackTime: FiniteDuration = hackTime
|
||||||
case 930 => "vanu_control_console"
|
|
||||||
case _ => throw new IllegalArgumentException("Not a valid capture terminal object id")
|
def FacilityHackTime_=(time: FiniteDuration): FiniteDuration = {
|
||||||
|
hackTime = time
|
||||||
|
FacilityHackTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package net.psforever.objects.serverobject.terminals.capture
|
package net.psforever.objects.serverobject.terminals.capture
|
||||||
|
|
||||||
|
import akka.util.Timeout
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||||
|
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
object CaptureTerminals {
|
object CaptureTerminals {import scala.concurrent.duration._
|
||||||
private val log = org.log4s.getLogger("CaptureTerminals")
|
private val log = org.log4s.getLogger("CaptureTerminals")
|
||||||
|
private implicit val timeout: Timeout = 1.second
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The process of hacking an object is completed.
|
* The process of hacking an object is completed.
|
||||||
|
|
@ -22,39 +25,34 @@ object CaptureTerminals {
|
||||||
def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Long)(): Unit = {
|
def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Long)(): Unit = {
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}")
|
log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}")
|
||||||
// Wait for the target actor to set the HackedBy property
|
// Wait for the target actor to set the HackedBy property
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
ask(target.Actor, CommonMessages.Hack(hackingPlayer, target))(1 second).mapTo[Boolean].onComplete {
|
ask(target.Actor, CommonMessages.Hack(hackingPlayer, target)).mapTo[Boolean].onComplete {
|
||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
val zone = target.Zone
|
||||||
target.Zone.id,
|
val zoneid = zone.id
|
||||||
|
val events = zone.LocalEvents
|
||||||
|
val isResecured = hackingPlayer.Faction == target.Faction
|
||||||
|
events ! LocalServiceMessage(
|
||||||
|
zoneid,
|
||||||
LocalAction.TriggerSound(hackingPlayer.GUID, target.HackSound, hackingPlayer.Position, 30, 0.49803925f)
|
LocalAction.TriggerSound(hackingPlayer.GUID, target.HackSound, hackingPlayer.Position, 30, 0.49803925f)
|
||||||
)
|
)
|
||||||
|
|
||||||
val isResecured = hackingPlayer.Faction == target.Faction
|
|
||||||
|
|
||||||
if (isResecured) {
|
if (isResecured) {
|
||||||
// Resecure the CC
|
// Resecure the CC
|
||||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
events ! LocalServiceMessage(
|
||||||
target.Zone.id,
|
zoneid,
|
||||||
LocalAction.ResecureCaptureTerminal(
|
LocalAction.ResecureCaptureTerminal(target, PlayerSource(hackingPlayer))
|
||||||
target
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Start the CC hack timer
|
// Start the CC hack timer
|
||||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
events ! LocalServiceMessage(
|
||||||
target.Zone.id,
|
zoneid,
|
||||||
LocalAction.StartCaptureTerminalHack(
|
LocalAction.StartCaptureTerminalHack(target)
|
||||||
target
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}")
|
case Failure(_) =>
|
||||||
|
log.warn(s"Hack message failed on target guid: ${target.GUID}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||||
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity, DamageableMountable}
|
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity, DamageableMountable}
|
||||||
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
|
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
|
||||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableEntity}
|
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity, RepairableEntity}
|
||||||
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
||||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
|
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
|
||||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||||
|
|
@ -27,13 +27,13 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
||||||
with RepairableEntity
|
with RepairableEntity
|
||||||
with AmenityAutoRepair
|
with AmenityAutoRepair
|
||||||
with CaptureTerminalAwareBehavior {
|
with CaptureTerminalAwareBehavior {
|
||||||
def MountableObject = mech
|
def MountableObject: ImplantTerminalMech = mech
|
||||||
def HackableObject = mech
|
def HackableObject: ImplantTerminalMech = mech
|
||||||
def FactionObject = mech
|
def FactionObject: ImplantTerminalMech = mech
|
||||||
def DamageableObject = mech
|
def DamageableObject: ImplantTerminalMech = mech
|
||||||
def RepairableObject = mech
|
def RepairableObject: ImplantTerminalMech = mech
|
||||||
def AutoRepairObject = mech
|
def AutoRepairObject: ImplantTerminalMech = mech
|
||||||
def CaptureTerminalAwareObject = mech
|
def CaptureTerminalAwareObject: ImplantTerminalMech = mech
|
||||||
|
|
||||||
def commonBehavior: Receive =
|
def commonBehavior: Receive =
|
||||||
checkBehavior
|
checkBehavior
|
||||||
|
|
@ -98,7 +98,6 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
||||||
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
|
override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
|
||||||
super.DestructionAwareness(target, cause)
|
super.DestructionAwareness(target, cause)
|
||||||
DamageableMountable.DestructionAwareness(DamageableObject, cause)
|
DamageableMountable.DestructionAwareness(DamageableObject, cause)
|
||||||
target.ClearHistory()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def PerformRepairs(target : Damageable.Target, amount : Int) : Int = {
|
override def PerformRepairs(target : Damageable.Target, amount : Int) : Int = {
|
||||||
|
|
@ -136,4 +135,9 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
||||||
def powerTurnOnCallback(): Unit = {
|
def powerTurnOnCallback(): Unit = {
|
||||||
tryAutoRepair()
|
tryAutoRepair()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def Restoration(obj: Target): Unit = {
|
||||||
|
super.Restoration(obj)
|
||||||
|
RepairableAmenity.RestorationOfHistory(obj)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.serverobject.turret
|
package net.psforever.objects.serverobject.turret
|
||||||
|
|
||||||
|
import akka.actor.Cancellable
|
||||||
import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool}
|
import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool}
|
||||||
import net.psforever.objects.equipment.{Ammo, JammableMountedWeapons}
|
import net.psforever.objects.equipment.{Ammo, JammableMountedWeapons}
|
||||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||||
|
|
@ -37,16 +38,16 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
with AmenityAutoRepair
|
with AmenityAutoRepair
|
||||||
with JammableMountedWeapons
|
with JammableMountedWeapons
|
||||||
with CaptureTerminalAwareBehavior {
|
with CaptureTerminalAwareBehavior {
|
||||||
def FactionObject = turret
|
def FactionObject: FacilityTurret = turret
|
||||||
def MountableObject = turret
|
def MountableObject: FacilityTurret = turret
|
||||||
def JammableObject = turret
|
def JammableObject: FacilityTurret = turret
|
||||||
def DamageableObject = turret
|
def DamageableObject: FacilityTurret = turret
|
||||||
def RepairableObject = turret
|
def RepairableObject: FacilityTurret = turret
|
||||||
def AutoRepairObject = turret
|
def AutoRepairObject: FacilityTurret = turret
|
||||||
def CaptureTerminalAwareObject = turret
|
def CaptureTerminalAwareObject: FacilityTurret = turret
|
||||||
|
|
||||||
// Used for timing ammo recharge for vanu turrets in caves
|
// Used for timing ammo recharge for vanu turrets in caves
|
||||||
var weaponAmmoRechargeTimer = Default.Cancellable
|
var weaponAmmoRechargeTimer: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = {
|
||||||
super.postStop()
|
super.postStop()
|
||||||
|
|
@ -186,7 +187,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
||||||
seat.unmount(player)
|
seat.unmount(player)
|
||||||
player.VehicleSeated = None
|
player.VehicleSeated = None
|
||||||
if (player.HasGUID) {
|
if (player.HasGUID) {
|
||||||
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, false, guid))
|
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2=false, guid))
|
||||||
}
|
}
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable
|
||||||
|
import net.psforever.objects.serverobject.hackable.Hackable.HackInfo
|
||||||
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
|
import net.psforever.objects.sourcing
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
|
import net.psforever.objects.vital.{Vitality, VitalityDefinition}
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class AmenitySource(
|
||||||
|
private val objdef: ObjectDefinition,
|
||||||
|
Faction: PlanetSideEmpire.Value,
|
||||||
|
health: Int,
|
||||||
|
Orientation: Vector3,
|
||||||
|
occupants: List[SourceEntry],
|
||||||
|
hacked: Option[HackInfo],
|
||||||
|
unique: UniqueAmenity
|
||||||
|
) extends SourceWithHealthEntry {
|
||||||
|
private val definition = objdef match {
|
||||||
|
case vital: VitalityDefinition => vital
|
||||||
|
case genericDefinition => NonvitalDefinition(genericDefinition)
|
||||||
|
}
|
||||||
|
private val modifiers = definition match {
|
||||||
|
case nonvital: NonvitalDefinition => nonvital
|
||||||
|
case _ => ObjectSource.FixedResistances
|
||||||
|
}
|
||||||
|
|
||||||
|
def Name: String = SourceEntry.NameFormat(definition.Descriptor)
|
||||||
|
def Definition: ObjectDefinition with VitalityDefinition = definition
|
||||||
|
def Health: Int = health
|
||||||
|
def total: Int = health
|
||||||
|
def Modifiers: ResistanceProfile = modifiers
|
||||||
|
def Position: Vector3 = unique.position
|
||||||
|
def Velocity: Option[Vector3] = None
|
||||||
|
}
|
||||||
|
|
||||||
|
object AmenitySource {
|
||||||
|
def apply(obj: Amenity): AmenitySource = {
|
||||||
|
val health: Int = obj match {
|
||||||
|
case o: Vitality => o.Health
|
||||||
|
case _ => 1
|
||||||
|
}
|
||||||
|
val hackData = obj match {
|
||||||
|
case o: Hackable => o.HackedBy
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
val amenity = AmenitySource(
|
||||||
|
obj.Definition,
|
||||||
|
obj.Faction,
|
||||||
|
health,
|
||||||
|
obj.Orientation,
|
||||||
|
Nil,
|
||||||
|
hackData,
|
||||||
|
sourcing.UniqueAmenity(obj.Zone.Number, obj.GUID, obj.Position)
|
||||||
|
)
|
||||||
|
amenity.copy(occupants = obj match {
|
||||||
|
case o: Mountable =>
|
||||||
|
o.Seats.values.flatMap { _.occupants }.map { p => PlayerSource.inSeat(p, o, amenity) }.toList
|
||||||
|
case _ =>
|
||||||
|
Nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
|
import net.psforever.objects.serverobject.structures.{Building, BuildingDefinition}
|
||||||
|
import net.psforever.objects.vital.VitalityDefinition
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
|
import net.psforever.types.{LatticeBenefit, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
|
final case class UniqueBuilding(
|
||||||
|
zone_number: Int,
|
||||||
|
building_guid: PlanetSideGUID
|
||||||
|
) extends SourceUniqueness
|
||||||
|
|
||||||
|
final case class BuildingSource(
|
||||||
|
private val obj_def: BuildingDefinition,
|
||||||
|
Faction: PlanetSideEmpire.Value,
|
||||||
|
Position: Vector3,
|
||||||
|
Orientation: Vector3,
|
||||||
|
benefits: Set[LatticeBenefit],
|
||||||
|
unique: UniqueBuilding
|
||||||
|
) extends SourceEntry {
|
||||||
|
private val definition = NonvitalDefinition(obj_def)
|
||||||
|
def Name: String = SourceEntry.NameFormat(Definition.Name)
|
||||||
|
def Definition: ObjectDefinition with VitalityDefinition = definition
|
||||||
|
def Modifiers: ResistanceProfile = ObjectSource.FixedResistances
|
||||||
|
def Velocity: Option[Vector3] = None
|
||||||
|
}
|
||||||
|
|
||||||
|
object BuildingSource {
|
||||||
|
def apply(b: Building): BuildingSource = {
|
||||||
|
BuildingSource(
|
||||||
|
b.Definition,
|
||||||
|
b.Faction,
|
||||||
|
b.Position,
|
||||||
|
b.Orientation,
|
||||||
|
b.latticeConnectedFacilityBenefits(),
|
||||||
|
UniqueBuilding(b.Zone.Number, b.GUID)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.definition.{DeployableDefinition, ObjectDefinition}
|
||||||
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class DeployableSource(
|
||||||
|
Definition: ObjectDefinition with DeployableDefinition,
|
||||||
|
Faction: PlanetSideEmpire.Value,
|
||||||
|
health: Int,
|
||||||
|
shields: Int,
|
||||||
|
owner: SourceEntry,
|
||||||
|
Position: Vector3,
|
||||||
|
Orientation: Vector3,
|
||||||
|
occupants: List[SourceEntry],
|
||||||
|
unique: UniqueDeployable
|
||||||
|
) extends SourceWithHealthEntry {
|
||||||
|
def Name: String = Definition.Descriptor
|
||||||
|
def Health: Int = health
|
||||||
|
def Shields: Int = shields
|
||||||
|
def OwnerName: String = owner.Name
|
||||||
|
def Velocity: Option[Vector3] = None
|
||||||
|
def Modifiers: ResistanceProfile = Definition.asInstanceOf[ResistanceProfile]
|
||||||
|
|
||||||
|
def total: Int = health + shields
|
||||||
|
}
|
||||||
|
|
||||||
|
object DeployableSource {
|
||||||
|
def apply(obj: Deployable): DeployableSource = {
|
||||||
|
val ownerName = obj.OwnerName
|
||||||
|
val ownerSource = (obj.Zone.LivePlayers ++ obj.Zone.Corpses)
|
||||||
|
.find { p => ownerName.contains(p.Name) }
|
||||||
|
match {
|
||||||
|
case Some(p) => SourceEntry(p)
|
||||||
|
case _ => SourceEntry.None
|
||||||
|
}
|
||||||
|
val occupants = obj match {
|
||||||
|
case o: Mountable =>
|
||||||
|
o.Seats.values.flatMap { _.occupants }.map { PlayerSource(_) }.toList
|
||||||
|
case _ =>
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
DeployableSource(
|
||||||
|
obj.Definition,
|
||||||
|
obj.Faction,
|
||||||
|
obj.Health,
|
||||||
|
obj.Shields,
|
||||||
|
ownerSource,
|
||||||
|
obj.Position,
|
||||||
|
obj.Orientation,
|
||||||
|
occupants,
|
||||||
|
UniqueDeployable(
|
||||||
|
obj.History.headOption match {
|
||||||
|
case Some(entry) => entry.time
|
||||||
|
case None => 0L
|
||||||
|
},
|
||||||
|
obj.OriginalOwnerName.getOrElse("none")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) 2017-2023 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
|
import net.psforever.objects.vital.VitalityDefinition
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper for a definition that does not represent a `Vitality` object
|
||||||
|
* but needs to look like one internally to satisfy type requirements.
|
||||||
|
* @param definition the original definition
|
||||||
|
*/
|
||||||
|
class NonvitalDefinition(private val definition : ObjectDefinition)
|
||||||
|
extends ObjectDefinition(definition.ObjectId)
|
||||||
|
with ResistanceProfileMutators
|
||||||
|
with VitalityDefinition {
|
||||||
|
Name = definition.Name
|
||||||
|
Packet = definition.Packet
|
||||||
|
|
||||||
|
def canEqual(a: Any) : Boolean = a.isInstanceOf[definition.type]
|
||||||
|
|
||||||
|
override def equals(that: Any): Boolean = definition.equals(that)
|
||||||
|
|
||||||
|
override def hashCode: Int = definition.hashCode
|
||||||
|
}
|
||||||
|
|
||||||
|
object NonvitalDefinition {
|
||||||
|
//single point of contact for all wrapped definitions
|
||||||
|
private val storage: scala.collection.mutable.LongMap[NonvitalDefinition] =
|
||||||
|
new scala.collection.mutable.LongMap[NonvitalDefinition]()
|
||||||
|
|
||||||
|
def apply(definition : ObjectDefinition) : NonvitalDefinition = {
|
||||||
|
storage.get(definition.ObjectId) match {
|
||||||
|
case Some(existing) =>
|
||||||
|
existing
|
||||||
|
case None =>
|
||||||
|
val out = new NonvitalDefinition(definition)
|
||||||
|
storage += definition.ObjectId.toLong -> out
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.vital.VitalityDefinition
|
||||||
|
import net.psforever.objects.vital.resistance.{ResistanceProfile, ResistanceProfileMutators}
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class UniqueObject(objectId: Int) extends SourceUniqueness
|
||||||
|
|
||||||
|
final case class ObjectSource(
|
||||||
|
private val obj_def: ObjectDefinition,
|
||||||
|
Faction: PlanetSideEmpire.Value,
|
||||||
|
Position: Vector3,
|
||||||
|
Orientation: Vector3,
|
||||||
|
Velocity: Option[Vector3],
|
||||||
|
unique: UniqueObject
|
||||||
|
) extends SourceEntry {
|
||||||
|
private val definition = obj_def match {
|
||||||
|
case vital : VitalityDefinition => vital
|
||||||
|
case genericDefinition => NonvitalDefinition(genericDefinition)
|
||||||
|
}
|
||||||
|
private val modifiers = definition match {
|
||||||
|
case nonvital : NonvitalDefinition => nonvital
|
||||||
|
case _ => ObjectSource.FixedResistances
|
||||||
|
}
|
||||||
|
def Name: String = SourceEntry.NameFormat(Definition.Name)
|
||||||
|
def Definition: ObjectDefinition with VitalityDefinition = definition
|
||||||
|
def Modifiers: ResistanceProfile = modifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
object ObjectSource {
|
||||||
|
final val FixedResistances = new ResistanceProfileMutators() { }
|
||||||
|
|
||||||
|
def apply(obj: PlanetSideGameObject): ObjectSource = {
|
||||||
|
ObjectSource(
|
||||||
|
obj.Definition,
|
||||||
|
obj match {
|
||||||
|
case aligned: FactionAffinity => aligned.Faction
|
||||||
|
case _ => PlanetSideEmpire.NEUTRAL
|
||||||
|
},
|
||||||
|
obj.Position,
|
||||||
|
obj.Orientation,
|
||||||
|
obj.Velocity,
|
||||||
|
UniqueObject(obj.Definition.ObjectId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
135
src/main/scala/net/psforever/objects/sourcing/PlayerSource.scala
Normal file
135
src/main/scala/net/psforever/objects/sourcing/PlayerSource.scala
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition}
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
|
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player}
|
||||||
|
import net.psforever.types.{CharacterSex, ExoSuitType, PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class UniquePlayer(
|
||||||
|
charId: Long,
|
||||||
|
name: String,
|
||||||
|
sex: CharacterSex,
|
||||||
|
faction: PlanetSideEmpire.Value
|
||||||
|
) extends SourceUniqueness
|
||||||
|
|
||||||
|
final case class PlayerSource(
|
||||||
|
Definition: AvatarDefinition,
|
||||||
|
ExoSuit: ExoSuitType.Value,
|
||||||
|
seatedIn: Option[(SourceEntry, Int)],
|
||||||
|
health: Int,
|
||||||
|
armor: Int,
|
||||||
|
Position: Vector3,
|
||||||
|
Orientation: Vector3,
|
||||||
|
Velocity: Option[Vector3],
|
||||||
|
crouching: Boolean,
|
||||||
|
jumping: Boolean,
|
||||||
|
Modifiers: ResistanceProfile,
|
||||||
|
bep: Long,
|
||||||
|
kills: Seq[Any],
|
||||||
|
unique: UniquePlayer
|
||||||
|
) extends SourceWithHealthEntry {
|
||||||
|
override def Name: String = unique.name
|
||||||
|
override def Faction: PlanetSideEmpire.Value = unique.faction
|
||||||
|
override def CharId: Long = unique.charId
|
||||||
|
|
||||||
|
def Seated: Boolean = seatedIn.nonEmpty
|
||||||
|
def Health: Int = health
|
||||||
|
def Armor: Int = armor
|
||||||
|
def total: Int = health + armor
|
||||||
|
}
|
||||||
|
|
||||||
|
object PlayerSource {
|
||||||
|
def apply(p: Player): PlayerSource = {
|
||||||
|
val exosuit = p.ExoSuit
|
||||||
|
val faction = p.Faction
|
||||||
|
val seatedEntity = mountableAndSeat(p)
|
||||||
|
PlayerSource(
|
||||||
|
p.Definition,
|
||||||
|
exosuit,
|
||||||
|
seatedEntity,
|
||||||
|
p.Health,
|
||||||
|
p.Armor,
|
||||||
|
p.Position,
|
||||||
|
p.Orientation,
|
||||||
|
p.Velocity,
|
||||||
|
p.Crouching,
|
||||||
|
p.Jumping,
|
||||||
|
ExoSuitDefinition.Select(exosuit, faction),
|
||||||
|
p.avatar.bep,
|
||||||
|
kills = Nil,
|
||||||
|
UniquePlayer(p.CharId, p.Name, p.Sex, faction)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(name: String, faction: PlanetSideEmpire.Value, position: Vector3): PlayerSource = {
|
||||||
|
new PlayerSource(
|
||||||
|
GlobalDefinitions.avatar,
|
||||||
|
ExoSuitType.Standard,
|
||||||
|
seatedIn = None,
|
||||||
|
health = 100,
|
||||||
|
armor = 0,
|
||||||
|
position,
|
||||||
|
Orientation = Vector3.Zero,
|
||||||
|
Velocity = None,
|
||||||
|
crouching = false,
|
||||||
|
jumping = false,
|
||||||
|
GlobalDefinitions.Standard,
|
||||||
|
bep = 0L,
|
||||||
|
kills = Nil,
|
||||||
|
UniquePlayer(0L, name, CharacterSex.Male, faction)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def mountableAndSeat(player: Player): Option[(SourceEntry, Int)] = {
|
||||||
|
player.Zone.GUID(player.VehicleSeated) match {
|
||||||
|
case Some(thing: PlanetSideGameObject with Mountable with FactionAffinity) =>
|
||||||
|
Some((SourceEntry(thing), thing.PassengerInSeat(player).get))
|
||||||
|
case _ =>
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a mostly normal player source entity
|
||||||
|
* but the `seatedIn` field is just a shallow copy of the mountable information.
|
||||||
|
* Said "shallow copy" will not reflect that the player is an occupant of the mountable entity
|
||||||
|
* even if this function is entirely for the purpose of establishing that the player is an occupant of the mountable entity.<br>
|
||||||
|
* Don't think too much about it.
|
||||||
|
* @param player player
|
||||||
|
* @param mount mountable entity in which the player should be seated
|
||||||
|
* @param source a `SourceEntry` for the aforementioned mountable entity
|
||||||
|
* @return a `PlayerSource` entity
|
||||||
|
*/
|
||||||
|
def inSeat(player: Player, mount: Mountable, source: SourceEntry): PlayerSource = {
|
||||||
|
val exosuit = player.ExoSuit
|
||||||
|
val faction = player.Faction
|
||||||
|
PlayerSource(
|
||||||
|
player.Definition,
|
||||||
|
exosuit,
|
||||||
|
Some((source, mount.PassengerInSeat(player).get)),
|
||||||
|
player.Health,
|
||||||
|
player.Armor,
|
||||||
|
player.Position,
|
||||||
|
player.Orientation,
|
||||||
|
player.Velocity,
|
||||||
|
player.Crouching,
|
||||||
|
player.Jumping,
|
||||||
|
ExoSuitDefinition.Select(exosuit, faction),
|
||||||
|
player.avatar.bep,
|
||||||
|
kills = Nil,
|
||||||
|
UniquePlayer(player.CharId, player.Name, player.Sex, faction)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Nobody is my name: Nobody they call me –
|
||||||
|
* my mother and my father and all my other companions”
|
||||||
|
* Thus I spoke but he immediately replied to me with a ruthless spirit:
|
||||||
|
* “I shall kill Nobody last of all, after his companions,
|
||||||
|
* the others first: this will be my guest-gift to you.”
|
||||||
|
*/
|
||||||
|
final val Nobody = PlayerSource("Nobody", PlanetSideEmpire.NEUTRAL, Vector3.Zero)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.ce.Deployable
|
||||||
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||||
|
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||||
|
import net.psforever.objects.vital.VitalityDefinition
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
|
import net.psforever.objects.{PlanetSideGameObject, Player, TurretDeployable, Vehicle}
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
trait SourceUniqueness
|
||||||
|
|
||||||
|
trait SourceEntry {
|
||||||
|
def Name: String
|
||||||
|
def Definition: ObjectDefinition with VitalityDefinition
|
||||||
|
def CharId: Long = 0L
|
||||||
|
def Faction: PlanetSideEmpire.Value
|
||||||
|
def Position: Vector3
|
||||||
|
def Orientation: Vector3
|
||||||
|
def Velocity: Option[Vector3]
|
||||||
|
def Modifiers: ResistanceProfile
|
||||||
|
def unique: SourceUniqueness
|
||||||
|
}
|
||||||
|
|
||||||
|
trait SourceWithHealthEntry extends SourceEntry {
|
||||||
|
def health: Int
|
||||||
|
def total: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
trait SourceWithShieldsEntry extends SourceWithHealthEntry {
|
||||||
|
def shields: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
object SourceEntry {
|
||||||
|
final protected val nonUnique: SourceUniqueness = new SourceUniqueness() { }
|
||||||
|
|
||||||
|
final val None = new SourceEntry() {
|
||||||
|
def Name: String = "none"
|
||||||
|
def Definition: ObjectDefinition with VitalityDefinition = null
|
||||||
|
def Faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||||
|
def Position: Vector3 = Vector3.Zero
|
||||||
|
def Orientation: Vector3 = Vector3.Zero
|
||||||
|
def Velocity: Option[Vector3] = Some(Vector3.Zero)
|
||||||
|
def Modifiers: ResistanceProfile = null
|
||||||
|
def unique: SourceUniqueness = nonUnique
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(target: PlanetSideGameObject with FactionAffinity): SourceEntry = {
|
||||||
|
target match {
|
||||||
|
case obj: Player => PlayerSource(obj)
|
||||||
|
case obj: Vehicle => VehicleSource(obj)
|
||||||
|
case obj: FacilityTurret => TurretSource(obj)
|
||||||
|
case obj: Amenity => AmenitySource(obj)
|
||||||
|
case obj: TurretDeployable => TurretSource(obj)
|
||||||
|
case obj: Deployable => DeployableSource(obj)
|
||||||
|
case obj: Building => BuildingSource(obj)
|
||||||
|
case _ => ObjectSource(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def NameFormat(name: String): String = {
|
||||||
|
name
|
||||||
|
.replace("_", " ")
|
||||||
|
.split(" ")
|
||||||
|
.map(_.capitalize)
|
||||||
|
.mkString(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
|
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
|
||||||
|
import net.psforever.objects.vital.VitalityDefinition
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
|
import net.psforever.objects.{PlanetSideGameObject, TurretDeployable}
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class TurretSource(
|
||||||
|
Definition: ObjectDefinition with VitalityDefinition,
|
||||||
|
Faction: PlanetSideEmpire.Value,
|
||||||
|
health: Int,
|
||||||
|
shields: Int,
|
||||||
|
Position: Vector3,
|
||||||
|
Orientation: Vector3,
|
||||||
|
occupants: List[SourceEntry],
|
||||||
|
unique: SourceUniqueness
|
||||||
|
) extends SourceWithHealthEntry with SourceWithShieldsEntry {
|
||||||
|
def Name: String = SourceEntry.NameFormat(Definition.Descriptor)
|
||||||
|
def Health: Int = health
|
||||||
|
def Shields: Int = shields
|
||||||
|
def Velocity: Option[Vector3] = None
|
||||||
|
def Modifiers: ResistanceProfile = Definition.asInstanceOf[ResistanceProfile]
|
||||||
|
|
||||||
|
def total: Int = health + shields
|
||||||
|
}
|
||||||
|
|
||||||
|
object TurretSource {
|
||||||
|
def apply(obj: PlanetSideGameObject with WeaponTurret): TurretSource = {
|
||||||
|
val position = obj.Position
|
||||||
|
val identifer = obj match {
|
||||||
|
case o: TurretDeployable =>
|
||||||
|
UniqueDeployable(
|
||||||
|
o.History.headOption match {
|
||||||
|
case Some(entry) => entry.time
|
||||||
|
case None => 0L
|
||||||
|
},
|
||||||
|
o.OriginalOwnerName.getOrElse("none")
|
||||||
|
)
|
||||||
|
case o: FacilityTurret =>
|
||||||
|
UniqueAmenity(o.Zone.Number, o.GUID, position)
|
||||||
|
case o =>
|
||||||
|
throw new IllegalArgumentException(s"was given ${o.Actor.toString()} when only wanted to model turrets")
|
||||||
|
}
|
||||||
|
val turret = TurretSource(
|
||||||
|
obj.Definition.asInstanceOf[ObjectDefinition with VitalityDefinition],
|
||||||
|
obj.Faction,
|
||||||
|
obj.Health,
|
||||||
|
shields = 0, //TODO implement later
|
||||||
|
position,
|
||||||
|
obj.Orientation,
|
||||||
|
Nil,
|
||||||
|
identifer
|
||||||
|
)
|
||||||
|
turret.copy(occupants = obj match {
|
||||||
|
case o: Mountable =>
|
||||||
|
o.Seats.values.flatMap { _.occupants }.map { p => PlayerSource.inSeat(p, o, turret) }.toList
|
||||||
|
case _ =>
|
||||||
|
Nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
|
final case class UniqueAmenity(
|
||||||
|
zoneNumber: Int,
|
||||||
|
guid: PlanetSideGUID,
|
||||||
|
position: Vector3
|
||||||
|
) extends SourceUniqueness
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
final case class UniqueDeployable(
|
||||||
|
spawnTime: Long,
|
||||||
|
originalOwnerName: String
|
||||||
|
) extends SourceUniqueness
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.objects.sourcing
|
||||||
|
|
||||||
|
import net.psforever.objects.Vehicle
|
||||||
|
import net.psforever.objects.definition.VehicleDefinition
|
||||||
|
import net.psforever.objects.vital.resistance.ResistanceProfile
|
||||||
|
import net.psforever.types.{DriveState, PlanetSideEmpire, Vector3}
|
||||||
|
|
||||||
|
final case class UniqueVehicle(spawnTime: Long, originalOwnerName: String) extends SourceUniqueness
|
||||||
|
|
||||||
|
final case class VehicleSource(
|
||||||
|
Definition: VehicleDefinition,
|
||||||
|
Faction: PlanetSideEmpire.Value,
|
||||||
|
health: Int,
|
||||||
|
shields: Int,
|
||||||
|
Position: Vector3,
|
||||||
|
Orientation: Vector3,
|
||||||
|
Velocity: Option[Vector3],
|
||||||
|
deployed: DriveState.Value,
|
||||||
|
occupants: List[SourceEntry],
|
||||||
|
Modifiers: ResistanceProfile,
|
||||||
|
unique: UniqueVehicle
|
||||||
|
) extends SourceWithHealthEntry with SourceWithShieldsEntry {
|
||||||
|
def Name: String = SourceEntry.NameFormat(Definition.Name)
|
||||||
|
def Health: Int = health
|
||||||
|
def Shields: Int = shields
|
||||||
|
def total: Int = health + shields
|
||||||
|
}
|
||||||
|
|
||||||
|
object VehicleSource {
|
||||||
|
def apply(obj: Vehicle): VehicleSource = {
|
||||||
|
val faction = obj.HackedBy match {
|
||||||
|
case Some(o) => o.player.Faction
|
||||||
|
case _ => obj.Faction
|
||||||
|
}
|
||||||
|
val vehicle = VehicleSource(
|
||||||
|
obj.Definition,
|
||||||
|
faction,
|
||||||
|
obj.Health,
|
||||||
|
obj.Shields,
|
||||||
|
obj.Position,
|
||||||
|
obj.Orientation,
|
||||||
|
obj.Velocity,
|
||||||
|
obj.DeploymentState,
|
||||||
|
Nil,
|
||||||
|
obj.Definition.asInstanceOf[ResistanceProfile],
|
||||||
|
UniqueVehicle(
|
||||||
|
obj.History.headOption match {
|
||||||
|
case Some(entry) => entry.time
|
||||||
|
case None => 0L
|
||||||
|
},
|
||||||
|
obj.OriginalOwnerName.getOrElse("none")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
vehicle.copy(occupants = {
|
||||||
|
obj.Seats.values.map { seat =>
|
||||||
|
seat.occupant match {
|
||||||
|
case Some(p) => PlayerSource.inSeat(p, obj, vehicle) //shallow
|
||||||
|
case _ => PlayerSource.Nobody
|
||||||
|
}
|
||||||
|
}.toList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
package net.psforever.objects.vehicles
|
package net.psforever.objects.vehicles
|
||||||
|
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.Vehicle
|
||||||
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality, SourceEntry}
|
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
|
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
|
||||||
import net.psforever.objects.vital.etc.RadiationReason
|
import net.psforever.objects.vital.etc.RadiationReason
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package net.psforever.objects.vehicles.control
|
||||||
|
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ballistics.VehicleSource
|
|
||||||
import net.psforever.objects.definition.{ToolDefinition, VehicleDefinition}
|
import net.psforever.objects.definition.{ToolDefinition, VehicleDefinition}
|
||||||
import net.psforever.objects.equipment._
|
import net.psforever.objects.equipment._
|
||||||
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
||||||
|
|
@ -11,8 +10,9 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec
|
||||||
import net.psforever.objects.serverobject.containable.ContainableBehavior
|
import net.psforever.objects.serverobject.containable.ContainableBehavior
|
||||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||||
import net.psforever.objects.serverobject.transfer.TransferBehavior
|
import net.psforever.objects.serverobject.transfer.TransferBehavior
|
||||||
|
import net.psforever.objects.sourcing.{SourceEntry, VehicleSource}
|
||||||
import net.psforever.objects.vehicles._
|
import net.psforever.objects.vehicles._
|
||||||
import net.psforever.objects.vital.VehicleShieldCharge
|
import net.psforever.objects.vital.ShieldCharge
|
||||||
import net.psforever.objects.vital.interaction.DamageResult
|
import net.psforever.objects.vital.interaction.DamageResult
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
|
|
@ -37,12 +37,12 @@ class BfrControl(vehicle: Vehicle)
|
||||||
* `Cancellable.alreadyCancelled` indicates a permanant cessation of charging activity (vehicle destruction) */
|
* `Cancellable.alreadyCancelled` indicates a permanant cessation of charging activity (vehicle destruction) */
|
||||||
var shieldCharge: Cancellable = Default.Cancellable
|
var shieldCharge: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
def SiphoningObject = vehicle
|
def SiphoningObject: Vehicle = vehicle
|
||||||
|
|
||||||
def ChargeTransferObject = vehicle
|
def ChargeTransferObject: Vehicle = vehicle
|
||||||
|
|
||||||
if (vehicle.Shields < vehicle.MaxShields) {
|
if (vehicle.Shields < vehicle.MaxShields) {
|
||||||
chargeShields(amount = 0) //start charging if starts as uncharged
|
chargeShields(amount = 0, Some(VehicleSource(vehicle))) //start charging if starts as uncharged
|
||||||
}
|
}
|
||||||
|
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = {
|
||||||
|
|
@ -107,7 +107,7 @@ class BfrControl(vehicle: Vehicle)
|
||||||
shieldCharge = context.system.scheduler.scheduleOnce(
|
shieldCharge = context.system.scheduler.scheduleOnce(
|
||||||
delay = vehicle.Definition.ShieldDamageDelay milliseconds,
|
delay = vehicle.Definition.ShieldDamageDelay milliseconds,
|
||||||
self,
|
self,
|
||||||
Vehicle.ChargeShields(0)
|
CommonMessages.ChargeShields(0, Some(vehicle))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +242,7 @@ class BfrControl(vehicle: Vehicle)
|
||||||
val (stow, _) = GridInventory.recoverInventory(afterInventory, vehicle.Inventory)
|
val (stow, _) = GridInventory.recoverInventory(afterInventory, vehicle.Inventory)
|
||||||
val afterWeapons = weapons
|
val afterWeapons = weapons
|
||||||
.map { item => item.start += 1; item }
|
.map { item => item.start += 1; item }
|
||||||
(culledWeaponMounts(pairedArmSubsys.unzip._2), afterWeapons, stow)
|
(culledWeaponMounts(pairedArmSubsys.map { _._2 }), afterWeapons, stow)
|
||||||
} else if(vWeapons.size == 2 && GlobalDefinitions.isBattleFrameGunnerVehicle(definition)) {
|
} else if(vWeapons.size == 2 && GlobalDefinitions.isBattleFrameGunnerVehicle(definition)) {
|
||||||
//battleframe is a flight variant but loadout spec is for gunner variant
|
//battleframe is a flight variant but loadout spec is for gunner variant
|
||||||
// remap the hands, shave the gunner mount from the spec, and refit the trunk
|
// remap the hands, shave the gunner mount from the spec, and refit the trunk
|
||||||
|
|
@ -312,7 +312,7 @@ class BfrControl(vehicle: Vehicle)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def chargeShields(amount: Int): Unit = {
|
override def chargeShields(amount: Int, motivator: Option[SourceEntry]): Unit = {
|
||||||
chargeShieldsOnly(amount)
|
chargeShieldsOnly(amount)
|
||||||
shieldCharge(vehicle.Shields, vehicle.Definition, delay = 0) //continue charge?
|
shieldCharge(vehicle.Shields, vehicle.Definition, delay = 0) //continue charge?
|
||||||
}
|
}
|
||||||
|
|
@ -328,7 +328,7 @@ class BfrControl(vehicle: Vehicle)
|
||||||
}).getOrElse(amount) * vehicle.SubsystemStatusMultiplier(sys = "BattleframeShieldGenerator.RechargeRate")).toInt)
|
}).getOrElse(amount) * vehicle.SubsystemStatusMultiplier(sys = "BattleframeShieldGenerator.RechargeRate")).toInt)
|
||||||
vehicle.Shields = before + chargeAmount
|
vehicle.Shields = before + chargeAmount
|
||||||
val after = vehicle.Shields
|
val after = vehicle.Shields
|
||||||
vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), after - before))
|
vehicle.LogActivity(ShieldCharge(after - before, Some(VehicleSource(vehicle))))
|
||||||
showShieldCharge()
|
showShieldCharge()
|
||||||
if (before == 0 && after > 0) {
|
if (before == 0 && after > 0) {
|
||||||
enableShield()
|
enableShield()
|
||||||
|
|
@ -346,7 +346,7 @@ class BfrControl(vehicle: Vehicle)
|
||||||
shieldCharge = context.system.scheduler.scheduleOnce(
|
shieldCharge = context.system.scheduler.scheduleOnce(
|
||||||
delay = definition.ShieldPeriodicDelay + delay milliseconds,
|
delay = definition.ShieldPeriodicDelay + delay milliseconds,
|
||||||
self,
|
self,
|
||||||
Vehicle.ChargeShields(0)
|
CommonMessages.ChargeShields(0, Some(vehicle))
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
shieldCharge = Default.Cancellable
|
shieldCharge = Default.Cancellable
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ package net.psforever.objects.vehicles.control
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
import net.psforever.objects._
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.ballistics.VehicleSource
|
|
||||||
import net.psforever.objects.definition.VehicleDefinition
|
import net.psforever.objects.definition.VehicleDefinition
|
||||||
import net.psforever.objects.definition.converter.OCM
|
import net.psforever.objects.definition.converter.OCM
|
||||||
import net.psforever.objects.entity.WorldEntity
|
import net.psforever.objects.entity.WorldEntity
|
||||||
|
|
@ -20,9 +19,10 @@ import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||||
import net.psforever.objects.serverobject.terminals.Terminal
|
import net.psforever.objects.serverobject.terminals.Terminal
|
||||||
|
import net.psforever.objects.sourcing.{SourceEntry, VehicleSource}
|
||||||
import net.psforever.objects.vehicles._
|
import net.psforever.objects.vehicles._
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.objects.vital.{DamagingActivity, VehicleShieldCharge, VitalsActivity}
|
import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge}
|
||||||
import net.psforever.objects.vital.environment.EnvironmentReason
|
import net.psforever.objects.vital.environment.EnvironmentReason
|
||||||
import net.psforever.objects.vital.etc.SuicideReason
|
import net.psforever.objects.vital.etc.SuicideReason
|
||||||
import net.psforever.objects.zones._
|
import net.psforever.objects.zones._
|
||||||
|
|
@ -60,23 +60,15 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
//make control actors belonging to utilities when making control actor belonging to vehicle
|
//make control actors belonging to utilities when making control actor belonging to vehicle
|
||||||
vehicle.Utilities.foreach { case (_, util) => util.Setup }
|
vehicle.Utilities.foreach { case (_, util) => util.Setup }
|
||||||
|
|
||||||
def MountableObject = vehicle
|
def MountableObject: Vehicle = vehicle
|
||||||
|
def JammableObject: Vehicle = vehicle
|
||||||
def JammableObject = vehicle
|
def FactionObject: Vehicle = vehicle
|
||||||
|
def DamageableObject: Vehicle = vehicle
|
||||||
def FactionObject = vehicle
|
def SiphonableObject: Vehicle = vehicle
|
||||||
|
def RepairableObject: Vehicle = vehicle
|
||||||
def DamageableObject = vehicle
|
def ContainerObject: Vehicle = vehicle
|
||||||
|
def InteractiveObject: Vehicle = vehicle
|
||||||
def SiphonableObject = vehicle
|
def CargoObject: Vehicle = vehicle
|
||||||
|
|
||||||
def RepairableObject = vehicle
|
|
||||||
|
|
||||||
def ContainerObject = vehicle
|
|
||||||
|
|
||||||
def InteractiveObject = vehicle
|
|
||||||
|
|
||||||
def CargoObject = vehicle
|
|
||||||
|
|
||||||
SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
|
SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
|
||||||
SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
|
SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
|
||||||
|
|
@ -136,8 +128,8 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
dismountBehavior.apply(msg)
|
dismountBehavior.apply(msg)
|
||||||
dismountCleanup(seat_num)
|
dismountCleanup(seat_num)
|
||||||
|
|
||||||
case Vehicle.ChargeShields(amount) =>
|
case CommonMessages.ChargeShields(amount, motivator) =>
|
||||||
chargeShields(amount)
|
chargeShields(amount, motivator.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) })
|
||||||
|
|
||||||
case Vehicle.UpdateZoneInteractionProgressUI(player) =>
|
case Vehicle.UpdateZoneInteractionProgressUI(player) =>
|
||||||
updateZoneInteractionProgressUI(player)
|
updateZoneInteractionProgressUI(player)
|
||||||
|
|
@ -406,6 +398,7 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
TaskWorkflow.execute(GUIDTask.unregisterVehicle(zone.GUID, vehicle))
|
TaskWorkflow.execute(GUIDTask.unregisterVehicle(zone.GUID, vehicle))
|
||||||
//banished to the shadow realm
|
//banished to the shadow realm
|
||||||
vehicle.Position = Vector3.Zero
|
vehicle.Position = Vector3.Zero
|
||||||
|
vehicle.ClearHistory()
|
||||||
//queue final deletion
|
//queue final deletion
|
||||||
decayTimer = context.system.scheduler.scheduleOnce(5 seconds, self, VehicleControl.Deletion())
|
decayTimer = context.system.scheduler.scheduleOnce(5 seconds, self, VehicleControl.Deletion())
|
||||||
}
|
}
|
||||||
|
|
@ -559,14 +552,14 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
|
|
||||||
//make certain vehicles don't charge shields too quickly
|
//make certain vehicles don't charge shields too quickly
|
||||||
def canChargeShields: Boolean = {
|
def canChargeShields: Boolean = {
|
||||||
val func: VitalsActivity => Boolean = VehicleControl.LastShieldChargeOrDamage(System.currentTimeMillis(), vehicle.Definition)
|
val func: InGameActivity => Boolean = VehicleControl.LastShieldChargeOrDamage(System.currentTimeMillis(), vehicle.Definition)
|
||||||
vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields &&
|
vehicle.Health > 0 && vehicle.Shields < vehicle.MaxShields &&
|
||||||
!vehicle.History.exists(func)
|
vehicle.History.findLast(func).isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
def chargeShields(amount: Int): Unit = {
|
def chargeShields(amount: Int, motivator: Option[SourceEntry]): Unit = {
|
||||||
if (canChargeShields) {
|
if (canChargeShields) {
|
||||||
vehicle.History(VehicleShieldCharge(VehicleSource(vehicle), amount))
|
vehicle.LogActivity(ShieldCharge(amount, motivator))
|
||||||
vehicle.Shields = vehicle.Shields + amount
|
vehicle.Shields = vehicle.Shields + amount
|
||||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
s"${vehicle.Actor}",
|
s"${vehicle.Actor}",
|
||||||
|
|
@ -874,7 +867,7 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
}
|
}
|
||||||
|
|
||||||
object VehicleControl {
|
object VehicleControl {
|
||||||
import net.psforever.objects.vital.{VehicleShieldCharge, VitalsActivity}
|
import net.psforever.objects.vital.{ShieldCharge}
|
||||||
|
|
||||||
private case class PrepareForDeletion()
|
private case class PrepareForDeletion()
|
||||||
|
|
||||||
|
|
@ -893,10 +886,10 @@ object VehicleControl {
|
||||||
* @return `true`, if the shield charge would be blocked;
|
* @return `true`, if the shield charge would be blocked;
|
||||||
* `false`, otherwise
|
* `false`, otherwise
|
||||||
*/
|
*/
|
||||||
def LastShieldChargeOrDamage(now: Long, vdef: VehicleDefinition)(act: VitalsActivity): Boolean = {
|
def LastShieldChargeOrDamage(now: Long, vdef: VehicleDefinition)(act: InGameActivity): Boolean = {
|
||||||
act match {
|
act match {
|
||||||
case dact: DamagingActivity => now - dact.time < vdef.ShieldDamageDelay //damage delays next charge
|
case dact: DamagingActivity => now - dact.time < vdef.ShieldDamageDelay //damage delays next charge
|
||||||
case vsc: VehicleShieldCharge => now - vsc.time < vdef.ShieldPeriodicDelay //previous charge delays next
|
case vsc: ShieldCharge => now - vsc.time < vdef.ShieldPeriodicDelay //previous charge delays next
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
241
src/main/scala/net/psforever/objects/vital/InGameHistory.scala
Normal file
241
src/main/scala/net/psforever/objects/vital/InGameHistory.scala
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
// Copyright (c) 2020 PSForever
|
||||||
|
package net.psforever.objects.vital
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.definition.{EquipmentDefinition, KitDefinition, ToolDefinition}
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.sourcing.{AmenitySource, ObjectSource, PlayerSource, SourceEntry, SourceWithHealthEntry, VehicleSource}
|
||||||
|
import net.psforever.objects.vital.environment.EnvironmentReason
|
||||||
|
import net.psforever.objects.vital.etc.{ExplodingEntityReason, PainboxReason, SuicideReason}
|
||||||
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
|
import net.psforever.types.{ExoSuitType, ImplantType, TransactionType}
|
||||||
|
|
||||||
|
/* root */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The root of all chronological activity.
|
||||||
|
* Must keep track of the time (ms) the activity occurred.
|
||||||
|
*/
|
||||||
|
trait InGameActivity {
|
||||||
|
val time: Long = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* normal history */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic classification of activity.
|
||||||
|
*/
|
||||||
|
trait GeneralActivity extends InGameActivity
|
||||||
|
|
||||||
|
final case class SpawningActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry]) extends GeneralActivity
|
||||||
|
|
||||||
|
final case class ReconstructionActivity(src: SourceEntry, zoneNumber: Int, unit: Option[SourceEntry]) extends GeneralActivity
|
||||||
|
|
||||||
|
final case class ShieldCharge(amount: Int, cause: Option[SourceEntry]) extends GeneralActivity
|
||||||
|
|
||||||
|
final case class TerminalUsedActivity(terminal: AmenitySource, transaction: TransactionType.Value) extends GeneralActivity
|
||||||
|
|
||||||
|
/* vitals history */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vital entity can be hurt or damaged or healed or repaired (HDHR).<br>
|
||||||
|
* Shields are not included in the definition of what is a "vital statistic",
|
||||||
|
* and that includes Infantry shields due to the Personal Shield implant
|
||||||
|
* and MAX shields due to being a New Conglomerate soldier.
|
||||||
|
*/
|
||||||
|
trait VitalsActivity extends InGameActivity {
|
||||||
|
def amount: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
trait HealingActivity extends VitalsActivity
|
||||||
|
|
||||||
|
trait RepairingActivity extends VitalsActivity
|
||||||
|
|
||||||
|
trait DamagingActivity extends VitalsActivity {
|
||||||
|
override val time: Long = data.interaction.hitTime
|
||||||
|
def data: DamageResult
|
||||||
|
|
||||||
|
def amount: Int = {
|
||||||
|
(data.targetBefore, data.targetAfter) match {
|
||||||
|
case (pb: SourceWithHealthEntry, pa: SourceWithHealthEntry) => pb.total - pa.total
|
||||||
|
case _ => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def health: Int = {
|
||||||
|
(data.targetBefore, data.targetAfter) match {
|
||||||
|
case (pb: PlayerSource, pa: PlayerSource) if pb.ExoSuit == ExoSuitType.MAX => pb.total - pa.total
|
||||||
|
case (pb: SourceWithHealthEntry, pa: SourceWithHealthEntry) => pb.health - pa.health
|
||||||
|
case _ => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final case class HealFromKit(kit_def: KitDefinition, amount: Int)
|
||||||
|
extends HealingActivity
|
||||||
|
|
||||||
|
final case class HealFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int)
|
||||||
|
extends HealingActivity
|
||||||
|
|
||||||
|
final case class HealFromTerm(term: AmenitySource, amount: Int)
|
||||||
|
extends HealingActivity
|
||||||
|
|
||||||
|
final case class HealFromImplant(implant: ImplantType, amount: Int)
|
||||||
|
extends HealingActivity
|
||||||
|
|
||||||
|
final case class RepairFromExoSuitChange(exosuit: ExoSuitType.Value, amount: Int)
|
||||||
|
extends RepairingActivity
|
||||||
|
|
||||||
|
final case class RepairFromKit(kit_def: KitDefinition, amount: Int)
|
||||||
|
extends RepairingActivity()
|
||||||
|
|
||||||
|
final case class RepairFromEquipment(user: PlayerSource, equipment_def: EquipmentDefinition, amount: Int)
|
||||||
|
extends RepairingActivity
|
||||||
|
|
||||||
|
final case class RepairFromTerm(term: AmenitySource, amount: Int) extends RepairingActivity
|
||||||
|
|
||||||
|
final case class RepairFromArmorSiphon(siphon_def: ToolDefinition, vehicle: VehicleSource, amount: Int)
|
||||||
|
extends RepairingActivity
|
||||||
|
|
||||||
|
final case class RepairFromAmenityAutoRepair(amount: Int)
|
||||||
|
extends RepairingActivity
|
||||||
|
|
||||||
|
final case class DamageFrom(data: DamageResult)
|
||||||
|
extends DamagingActivity
|
||||||
|
|
||||||
|
final case class DamageFromProjectile(data: DamageResult)
|
||||||
|
extends DamagingActivity
|
||||||
|
|
||||||
|
final case class DamageFromPainbox(data: DamageResult)
|
||||||
|
extends DamagingActivity
|
||||||
|
|
||||||
|
final case class DamageFromEnvironment(data: DamageResult)
|
||||||
|
extends DamagingActivity
|
||||||
|
|
||||||
|
final case class PlayerSuicide(player: PlayerSource)
|
||||||
|
extends DamagingActivity {
|
||||||
|
private lazy val result = {
|
||||||
|
val out = DamageResult(
|
||||||
|
player,
|
||||||
|
player.copy(health = 0),
|
||||||
|
DamageInteraction(player, SuicideReason(), player.Position)
|
||||||
|
)
|
||||||
|
out
|
||||||
|
}
|
||||||
|
def data: DamageResult = result
|
||||||
|
}
|
||||||
|
|
||||||
|
final case class DamageFromExplodingEntity(data: DamageResult)
|
||||||
|
extends DamagingActivity
|
||||||
|
|
||||||
|
|
||||||
|
trait InGameHistory {
|
||||||
|
/** a list of important events that have occurred in chronological order */
|
||||||
|
private var history: List[InGameActivity] = List.empty[InGameActivity]
|
||||||
|
/** the last source of damage that cna be used to indicate blame for damage */
|
||||||
|
private var lastDamage: Option[DamageResult] = None
|
||||||
|
|
||||||
|
def History: List[InGameActivity] = history
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only the changes to vitality statistics.
|
||||||
|
* @return a list of the chronologically-consistent vitality events
|
||||||
|
*/
|
||||||
|
def VitalsHistory(): List[VitalsActivity] = History.collect {
|
||||||
|
case event: VitalsActivity => event
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An in-game event must be recorded.
|
||||||
|
* Add new entry to the front of the list (for recent activity).
|
||||||
|
* @param action the fully-informed entry
|
||||||
|
* @return the list of previous changes to this entity
|
||||||
|
*/
|
||||||
|
def LogActivity(action: InGameActivity): List[InGameActivity] = LogActivity(Some(action))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An in-game event must be recorded.
|
||||||
|
* Add new entry to the front of the list (for recent activity).
|
||||||
|
* @param action the fully-informed entry
|
||||||
|
* @return the list of previous changes to this entity
|
||||||
|
*/
|
||||||
|
def LogActivity(action: Option[InGameActivity]): List[InGameActivity] = {
|
||||||
|
action match {
|
||||||
|
case Some(act) =>
|
||||||
|
history = act +: history
|
||||||
|
case None => ()
|
||||||
|
}
|
||||||
|
history
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Very common example of a `VitalsActivity` event involving damage.
|
||||||
|
* They are repackaged before submission and are often tagged for specific blame.
|
||||||
|
* @param result the fully-informed entry
|
||||||
|
* @return the list of previous changes to this object's vital statistics
|
||||||
|
*/
|
||||||
|
def LogActivity(result: DamageResult): List[InGameActivity] = {
|
||||||
|
result.interaction.cause match {
|
||||||
|
case _: ProjectileReason =>
|
||||||
|
LogActivity(DamageFromProjectile(result))
|
||||||
|
lastDamage = Some(result)
|
||||||
|
case _: ExplodingEntityReason =>
|
||||||
|
LogActivity(DamageFromExplodingEntity(result))
|
||||||
|
lastDamage = Some(result)
|
||||||
|
case _: PainboxReason =>
|
||||||
|
LogActivity(DamageFromPainbox(result))
|
||||||
|
case _: EnvironmentReason =>
|
||||||
|
LogActivity(DamageFromEnvironment(result))
|
||||||
|
case _ => ;
|
||||||
|
LogActivity(DamageFrom(result))
|
||||||
|
if(result.adversarial.nonEmpty) {
|
||||||
|
lastDamage = Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
History
|
||||||
|
}
|
||||||
|
|
||||||
|
def LastDamage: Option[DamageResult] = lastDamage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find, specifically, the last instance of a weapon discharge that caused damage.
|
||||||
|
* @return information about the discharge
|
||||||
|
*/
|
||||||
|
def LastShot: Option[DamageResult] = {
|
||||||
|
History.findLast({ p => p.isInstanceOf[DamageFromProjectile] }).map {
|
||||||
|
case entry: DamageFromProjectile => entry.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def ClearHistory(): List[InGameActivity] = {
|
||||||
|
lastDamage = None
|
||||||
|
val out = history
|
||||||
|
history = List.empty
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object InGameHistory {
|
||||||
|
def SpawnReconstructionActivity(
|
||||||
|
obj: PlanetSideGameObject with FactionAffinity with InGameHistory,
|
||||||
|
zoneNumber: Int,
|
||||||
|
unit: Option[SourceEntry]
|
||||||
|
): Unit = {
|
||||||
|
val event: GeneralActivity = if (obj.History.nonEmpty || obj.History.headOption.exists {
|
||||||
|
_.isInstanceOf[SpawningActivity]
|
||||||
|
}) {
|
||||||
|
ReconstructionActivity(ObjectSource(obj), zoneNumber, unit)
|
||||||
|
} else {
|
||||||
|
SpawningActivity(ObjectSource(obj), zoneNumber, unit)
|
||||||
|
}
|
||||||
|
if (obj.History.lastOption match {
|
||||||
|
case Some(evt: SpawningActivity) => evt != event
|
||||||
|
case Some(evt: ReconstructionActivity) => evt != event
|
||||||
|
case _ => true
|
||||||
|
}) {
|
||||||
|
obj.LogActivity(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.vital.resolution.{DamageAndResistance, ResolutionCa
|
||||||
* The amount of HDHR is controlled by the damage model of this vital object reacting to stimulus.
|
* The amount of HDHR is controlled by the damage model of this vital object reacting to stimulus.
|
||||||
* The damage model is provided.
|
* The damage model is provided.
|
||||||
*/
|
*/
|
||||||
trait Vitality extends VitalsHistory {
|
trait Vitality extends InGameHistory {
|
||||||
private var health: Int = Definition.DefaultHealth
|
private var health: Int = Definition.DefaultHealth
|
||||||
private var defaultHealth: Option[Int] = None
|
private var defaultHealth: Option[Int] = None
|
||||||
private var maxHealth: Option[Int] = None
|
private var maxHealth: Option[Int] = None
|
||||||
|
|
|
||||||
|
|
@ -1,237 +0,0 @@
|
||||||
// Copyright (c) 2020 PSForever
|
|
||||||
package net.psforever.objects.vital
|
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, VehicleSource}
|
|
||||||
import net.psforever.objects.definition.{EquipmentDefinition, KitDefinition, ToolDefinition}
|
|
||||||
import net.psforever.objects.serverobject.terminals.TerminalDefinition
|
|
||||||
import net.psforever.objects.vital.environment.EnvironmentReason
|
|
||||||
import net.psforever.objects.vital.etc.{ExplodingEntityReason, PainboxReason, SuicideReason}
|
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
|
||||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
|
||||||
|
|
||||||
trait VitalsActivity {
|
|
||||||
def time: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
trait HealingActivity extends VitalsActivity {
|
|
||||||
def amount: Int
|
|
||||||
val time: Long = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
|
|
||||||
trait DamagingActivity extends VitalsActivity {
|
|
||||||
def time: Long = data.interaction.hitTime
|
|
||||||
def data: DamageResult
|
|
||||||
}
|
|
||||||
|
|
||||||
final case class HealFromKit(kit_def: KitDefinition, amount: Int)
|
|
||||||
extends HealingActivity
|
|
||||||
|
|
||||||
final case class HealFromEquipment(
|
|
||||||
user: PlayerSource,
|
|
||||||
equipment_def: EquipmentDefinition,
|
|
||||||
amount: Int
|
|
||||||
) extends HealingActivity
|
|
||||||
|
|
||||||
final case class HealFromTerm(term_def: TerminalDefinition, health: Int, armor: Int)
|
|
||||||
extends HealingActivity {
|
|
||||||
def amount: Int = health + armor
|
|
||||||
}
|
|
||||||
|
|
||||||
final case class HealFromImplant(implant: ImplantType, health: Int)
|
|
||||||
extends HealingActivity {
|
|
||||||
def amount: Int = health
|
|
||||||
}
|
|
||||||
|
|
||||||
final case class HealFromExoSuitChange(exosuit: ExoSuitType.Value)
|
|
||||||
extends HealingActivity {
|
|
||||||
def amount: Int = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
final case class RepairFromKit(kit_def: KitDefinition, amount: Int)
|
|
||||||
extends HealingActivity()
|
|
||||||
|
|
||||||
final case class RepairFromEquipment(
|
|
||||||
user: PlayerSource,
|
|
||||||
equipment_def: EquipmentDefinition,
|
|
||||||
amount: Int
|
|
||||||
) extends HealingActivity
|
|
||||||
|
|
||||||
final case class RepairFromTerm(term_def: TerminalDefinition, amount: Int)
|
|
||||||
extends HealingActivity
|
|
||||||
|
|
||||||
final case class RepairFromArmorSiphon(siphon_def: ToolDefinition, amount: Int)
|
|
||||||
extends HealingActivity
|
|
||||||
|
|
||||||
final case class VehicleShieldCharge(amount: Int)
|
|
||||||
extends HealingActivity //TODO facility
|
|
||||||
|
|
||||||
final case class DamageFrom(data: DamageResult)
|
|
||||||
extends DamagingActivity
|
|
||||||
|
|
||||||
final case class DamageFromProjectile(data: DamageResult)
|
|
||||||
extends DamagingActivity
|
|
||||||
|
|
||||||
final case class DamageFromPainbox(data: DamageResult)
|
|
||||||
extends DamagingActivity
|
|
||||||
|
|
||||||
final case class DamageFromEnvironment(data: DamageResult)
|
|
||||||
extends DamagingActivity
|
|
||||||
|
|
||||||
final case class PlayerSuicide(player: PlayerSource)
|
|
||||||
extends DamagingActivity {
|
|
||||||
private lazy val result = {
|
|
||||||
val out = DamageResult(
|
|
||||||
player,
|
|
||||||
player.copy(health = 0),
|
|
||||||
DamageInteraction(player, SuicideReason(), player.Position)
|
|
||||||
)
|
|
||||||
out
|
|
||||||
}
|
|
||||||
def data: DamageResult = result
|
|
||||||
}
|
|
||||||
|
|
||||||
final case class DamageFromExplodingEntity(data: DamageResult)
|
|
||||||
extends DamagingActivity
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A vital object can be hurt or damaged or healed or repaired (HDHR).
|
|
||||||
* A history of the previous changes in vital statistics of the underlying object is recorded
|
|
||||||
* in reverse chronological order.
|
|
||||||
*/
|
|
||||||
trait VitalsHistory {
|
|
||||||
|
|
||||||
/** a reverse-order list of chronological events that have occurred to these vital statistics */
|
|
||||||
private var vitalsHistory: List[VitalsActivity] = List.empty[VitalsActivity]
|
|
||||||
|
|
||||||
private var lastDamage: Option[DamageResult] = None
|
|
||||||
|
|
||||||
def History: List[VitalsActivity] = vitalsHistory
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A `VitalsActivity` event must be recorded.
|
|
||||||
* Add new entry to the front of the list (for recent activity).
|
|
||||||
* @param action the fully-informed entry
|
|
||||||
* @return the list of previous changes to this object's vital statistics
|
|
||||||
*/
|
|
||||||
def History(action: VitalsActivity): List[VitalsActivity] = History(Some(action))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A `VitalsActivity` event must be recorded.
|
|
||||||
* Add new entry to the front of the list (for recent activity).
|
|
||||||
* @param action the fully-informed entry
|
|
||||||
* @return the list of previous changes to this object's vital statistics
|
|
||||||
*/
|
|
||||||
def History(action: Option[VitalsActivity]): List[VitalsActivity] = {
|
|
||||||
action match {
|
|
||||||
case Some(act) =>
|
|
||||||
vitalsHistory = act +: vitalsHistory
|
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
vitalsHistory
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Very common example of a `VitalsActivity` event involving damage.
|
|
||||||
* @param result the fully-informed entry
|
|
||||||
* @return the list of previous changes to this object's vital statistics
|
|
||||||
*/
|
|
||||||
def History(result: DamageResult): List[VitalsActivity] = {
|
|
||||||
result.interaction.cause match {
|
|
||||||
case _: ProjectileReason =>
|
|
||||||
vitalsHistory = DamageFromProjectile(result) +: vitalsHistory
|
|
||||||
lastDamage = Some(result)
|
|
||||||
case _: ExplodingEntityReason =>
|
|
||||||
vitalsHistory = DamageFromExplodingEntity(result) +: vitalsHistory
|
|
||||||
lastDamage = Some(result)
|
|
||||||
case _: PainboxReason =>
|
|
||||||
vitalsHistory = DamageFromPainbox(result) +: vitalsHistory
|
|
||||||
case _: EnvironmentReason =>
|
|
||||||
vitalsHistory = DamageFromEnvironment(result) +: vitalsHistory
|
|
||||||
case _ => ;
|
|
||||||
vitalsHistory = DamageFrom(result) +: vitalsHistory
|
|
||||||
if(result.adversarial.nonEmpty) {
|
|
||||||
lastDamage = Some(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vitalsHistory
|
|
||||||
}
|
|
||||||
|
|
||||||
def LastDamage: Option[DamageResult] = lastDamage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find, specifically, the last instance of a weapon discharge vital statistics change.
|
|
||||||
* @return information about the discharge
|
|
||||||
*/
|
|
||||||
def LastShot: Option[DamageResult] = {
|
|
||||||
vitalsHistory.find({ p => p.isInstanceOf[DamageFromProjectile] }) match {
|
|
||||||
case Some(entry: DamageFromProjectile) =>
|
|
||||||
Some(entry.data)
|
|
||||||
case _ =>
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def ClearHistory(): List[VitalsActivity] = {
|
|
||||||
val out = vitalsHistory
|
|
||||||
vitalsHistory = List.empty[VitalsActivity]
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//deprecated overrides
|
|
||||||
object HealFromKit {
|
|
||||||
def apply(Target: PlayerSource, amount: Int, kit_def: KitDefinition): HealFromKit =
|
|
||||||
HealFromKit(kit_def, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
object HealFromEquipment {
|
|
||||||
def apply(
|
|
||||||
Target: PlayerSource,
|
|
||||||
user: PlayerSource,
|
|
||||||
amount: Int,
|
|
||||||
equipment_def: EquipmentDefinition
|
|
||||||
): HealFromEquipment =
|
|
||||||
HealFromEquipment(user, equipment_def, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
object HealFromTerm {
|
|
||||||
def apply(Target: PlayerSource, health: Int, armor: Int, term_def: TerminalDefinition): HealFromTerm =
|
|
||||||
HealFromTerm(term_def, health, armor)
|
|
||||||
}
|
|
||||||
|
|
||||||
object HealFromImplant {
|
|
||||||
def apply(Target: PlayerSource, amount: Int, implant: ImplantType): HealFromImplant =
|
|
||||||
HealFromImplant(implant, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
object HealFromExoSuitChange {
|
|
||||||
def apply(Target: PlayerSource, exosuit: ExoSuitType.Value): HealFromExoSuitChange =
|
|
||||||
HealFromExoSuitChange(exosuit)
|
|
||||||
}
|
|
||||||
|
|
||||||
object RepairFromKit {
|
|
||||||
def apply(Target: PlayerSource, amount: Int, kit_def: KitDefinition): RepairFromKit =
|
|
||||||
RepairFromKit(kit_def, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
object RepairFromEquipment {
|
|
||||||
def apply(
|
|
||||||
Target: PlayerSource,
|
|
||||||
user: PlayerSource,
|
|
||||||
amount: Int,
|
|
||||||
equipment_def: EquipmentDefinition
|
|
||||||
) : RepairFromEquipment =
|
|
||||||
RepairFromEquipment(user, equipment_def, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
object RepairFromTerm {
|
|
||||||
def apply(Target: VehicleSource, amount: Int, term_def: TerminalDefinition): RepairFromTerm =
|
|
||||||
RepairFromTerm(term_def, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
object VehicleShieldCharge {
|
|
||||||
def apply(Target: VehicleSource, amount: Int): VehicleShieldCharge =
|
|
||||||
VehicleShieldCharge(amount)
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.vital.base
|
package net.psforever.objects.vital.base
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.damage.DamageProfile
|
import net.psforever.objects.vital.damage.DamageProfile
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.prop.DamageProperties
|
import net.psforever.objects.vital.prop.DamageProperties
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) 2021 PSForever
|
// Copyright (c) 2021 PSForever
|
||||||
package net.psforever.objects.vital.collision
|
package net.psforever.objects.vital.collision
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.{DeployableSource, PlayerSource, VehicleSource}
|
|
||||||
import net.psforever.objects.definition.ExoSuitDefinition
|
import net.psforever.objects.definition.ExoSuitDefinition
|
||||||
|
import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, VehicleSource}
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.vital.collision
|
package net.psforever.objects.vital.collision
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.{DeployableSource, SourceEntry, VehicleSource}
|
import net.psforever.objects.sourcing.{DeployableSource, SourceEntry, VehicleSource}
|
||||||
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason, DamageResolution, DamageType}
|
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason, DamageResolution, DamageType}
|
||||||
import net.psforever.objects.vital.prop.DamageProperties
|
import net.psforever.objects.vital.prop.DamageProperties
|
||||||
import net.psforever.objects.vital.resolution.DamageAndResistance
|
import net.psforever.objects.vital.resolution.DamageAndResistance
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package net.psforever.objects.vital.damage
|
package net.psforever.objects.vital.damage
|
||||||
|
|
||||||
import net.psforever.objects.GlobalDefinitions
|
import net.psforever.objects.GlobalDefinitions
|
||||||
import net.psforever.objects.ballistics._
|
import net.psforever.objects.sourcing.VehicleSource
|
||||||
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason}
|
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason}
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.prop.DamageWithPosition
|
import net.psforever.objects.vital.prop.DamageWithPosition
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.vital.environment
|
package net.psforever.objects.vital.environment
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.PlayerSource
|
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.serverobject.environment.EnvironmentAttribute
|
import net.psforever.objects.serverobject.environment.EnvironmentAttribute
|
||||||
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The deeper you move into lava, the greater the amount of health you burn through.
|
* The deeper you move into lava, the greater the amount of health you burn through.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.vital.environment
|
package net.psforever.objects.vital.environment
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, PieceOfEnvironment}
|
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, PieceOfEnvironment}
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
||||||
import net.psforever.objects.vital.damage.DamageCalculations
|
import net.psforever.objects.vital.damage.DamageCalculations
|
||||||
import net.psforever.objects.vital.prop.DamageProperties
|
import net.psforever.objects.vital.prop.DamageProperties
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) 2021 PSForever
|
// Copyright (c) 2021 PSForever
|
||||||
package net.psforever.objects.vital.etc
|
package net.psforever.objects.vital.etc
|
||||||
|
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.{GlobalDefinitions, Tool, Vehicle}
|
import net.psforever.objects.{GlobalDefinitions, Tool, Vehicle}
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason, DamageResolution}
|
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason, DamageResolution}
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.prop.DamageWithPosition
|
import net.psforever.objects.vital.prop.DamageWithPosition
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
package net.psforever.objects.vital.etc
|
package net.psforever.objects.vital.etc
|
||||||
|
|
||||||
import net.psforever.objects.PlanetSideGameObject
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
||||||
import net.psforever.objects.vital.prop.DamageWithPosition
|
import net.psforever.objects.vital.prop.DamageWithPosition
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
package net.psforever.objects.vital.etc
|
package net.psforever.objects.vital.etc
|
||||||
|
|
||||||
import net.psforever.objects.PlanetSideGameObject
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.{Vitality, VitalityDefinition}
|
import net.psforever.objects.vital.{Vitality, VitalityDefinition}
|
||||||
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason, DamageResolution}
|
import net.psforever.objects.vital.base.{DamageModifiers, DamageReason, DamageResolution}
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.vital.etc
|
package net.psforever.objects.vital.etc
|
||||||
|
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
|
||||||
import net.psforever.objects.serverobject.painbox.Painbox
|
import net.psforever.objects.serverobject.painbox.Painbox
|
||||||
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
|
import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
|
||||||
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
||||||
import net.psforever.objects.vital.damage.DamageCalculations
|
import net.psforever.objects.vital.damage.DamageCalculations
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue