From 4261693ae178232ee05424c86f94cba6c750ac16 Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 3 Jan 2020 11:25:31 -0500 Subject: [PATCH 1/5] sphere of influence is now activated by zone denizens; painboxes are activated by sphere of influence denizens --- .../serverobject/painbox/Painbox.scala | 2 + .../serverobject/painbox/PainboxControl.scala | 21 ++++- .../serverobject/structures/Building.scala | 13 ++++ .../terminals/ProximityTerminalControl.scala | 13 +--- .../zones/SphereOfInfluenceActor.scala | 78 +++++++++++++------ .../net/psforever/objects/zones/Zone.scala | 39 ++++++---- .../objects/zones/ZonePopulationActor.scala | 9 ++- 7 files changed, 121 insertions(+), 54 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/painbox/Painbox.scala b/common/src/main/scala/net/psforever/objects/serverobject/painbox/Painbox.scala index 5bbe3128d..3fa6d291b 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/painbox/Painbox.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/painbox/Painbox.scala @@ -10,7 +10,9 @@ class Painbox(tdef : PainboxDefinition) extends Amenity { } object Painbox { + final case class Start() final case class Tick() + final case class Stop() def apply(tdef : PainboxDefinition) : Painbox = { new Painbox(tdef) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala index 2a32054b5..aad8f4e9a 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala @@ -22,15 +22,19 @@ class PainboxControl(painbox: Painbox) extends Actor { nearestDoor = obj.Amenities .collect { case door : Door => door } .minBy(door => Vector3.DistanceSquared(painbox.Position, door.Position)) - painboxTick = context.system.scheduler.schedule(0 seconds,1 second, self, Painbox.Tick()) - context.become(Processing) + context.become(Stopped) case _ => ; } case _ => ; } - def Processing : Receive = { + def Running : Receive = { + case Painbox.Stop() => + context.become(Stopped) + painboxTick.cancel + painboxTick = DefaultCancellable.obj + case Painbox.Tick() => //todo: Account for overlapping pain fields //todo: Pain module @@ -49,5 +53,16 @@ class PainboxControl(painbox: Painbox) extends Actor { events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, damage)) } } + + case _ => ; + } + + def Stopped : Receive = { + case Painbox.Start() => + context.become(Running) + painboxTick.cancel + painboxTick = context.system.scheduler.schedule(0 seconds, 1 second, self, Painbox.Tick()) + + case _ => ; } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala index 6dcd285d6..7fa0ab898 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -7,6 +7,7 @@ import akka.actor.{ActorContext, ActorRef} import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.serverobject.hackable.Hackable +import net.psforever.objects.serverobject.painbox.Painbox import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.terminals.CaptureTerminal import net.psforever.objects.serverobject.tube.SpawnTube @@ -56,6 +57,18 @@ class Building(private val name: String, def PlayersInSOI : List[Player] = playersInSOI def PlayersInSOI_=(list : List[Player]) : List[Player] = { + if(playersInSOI.isEmpty && list.nonEmpty) { + Amenities.collect { + case box : Painbox => + box.Actor ! Painbox.Start() + } + } + else if(playersInSOI.nonEmpty && list.isEmpty) { + Amenities.collect { + case box : Painbox => + box.Actor ! Painbox.Stop() + } + } playersInSOI = list playersInSOI } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index c7b2e742e..34c4df244 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -6,7 +6,6 @@ import net.psforever.objects._ import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.hackable.HackableBehavior -import services.Service import scala.collection.mutable import scala.concurrent.duration._ @@ -27,17 +26,7 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor def TerminalObject : Terminal with ProximityUnit = term - def receive : Receive = Start - - def Start : Receive = checkBehavior - .orElse { - case Service.Startup() => - context.become(Run) - - case _ => ; - } - - def Run : Receive = checkBehavior + def receive : Receive = checkBehavior .orElse(hackableBehavior) .orElse { case CommonMessages.Use(_, Some(target : PlanetSideGameObject)) => diff --git a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala index a34ed5403..844c643b3 100644 --- a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala @@ -1,45 +1,77 @@ -package net.psforever.objects.zones; +package net.psforever.objects.zones + import akka.actor.{Actor, Cancellable} +import net.psforever.objects.{DefaultCancellable, Player} import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.serverobject.structures.{Building, SphereOfInfluence} +import net.psforever.types.Vector3 +import scala.annotation.tailrec import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ class SphereOfInfluenceActor(zone: Zone) extends Actor { - def receive : Receive = Established + var sois : Iterable[(Building, Int)] = Nil + var populateTick : Cancellable = DefaultCancellable.obj + //private[this] val log = org.log4s.getLogger(s"${zone.Id.capitalize}-SphereOfInfluenceActor") - private var populateTick: Cancellable = context.system.scheduler.scheduleOnce(5 seconds, self, SOI.Populate()) - private[this] val log = org.log4s.getLogger(s"${zone.Id.capitalize}-SphereOfInfluenceActor") + def receive : Receive = Stopped - def Established : Receive = { + def Build : Receive = { + case SOI.Build() => + BuildSOI() + } + + def Running : Receive = Build.orElse { case SOI.Populate() => UpdateSOI() + + case SOI.StopPopulation() => + context.become(Stopped) + populateTick.cancel + sois.foreach { case (facility, _) => facility.PlayersInSOI = Nil } + + case _ => ; + } + + def Stopped : Receive = Build.orElse { + case SOI.Populate() => + context.become(Running) + UpdateSOI() + + case _ => ; + } + + def BuildSOI() : Unit = { + sois = zone.Buildings + .values + .map { facility => (facility, facility.Definition) } + .collect { case (facility, soi : ObjectDefinition with SphereOfInfluence) if soi.SOIRadius > 0 => + (facility, soi.SOIRadius * soi.SOIRadius) + } } def UpdateSOI(): Unit = { - val players = zone.LivePlayers - - zone.Buildings.foreach({ - case (_, building : Building) => - building.Definition match { - case _ : ObjectDefinition with SphereOfInfluence => - // todo: overlapping soi (e.g. tower soi in base soi) order by smallest soi first? - val playersInSoi = players.filter(p => Math.pow(p.Position.x - building.Position.x, 2) + Math.pow(p.Position.y - building.Position.y, 2) < Math.pow(300, 2) ) - if(playersInSoi.length > 0) { -// log.info(s"Building ${building.GUID} players in soi: ${playersInSoi.toString()}" ) - } - building.PlayersInSOI = playersInSoi - case _ => ; - } - - }) - + SOI.Populate(sois.iterator, zone.LivePlayers) populateTick = context.system.scheduler.scheduleOnce(5 seconds, self, SOI.Populate()) } } object SOI { + /** Rebuild the list of facility SOI data **/ + final case class Build() /** Populate the list of players within a SOI **/ final case class Populate() -} \ No newline at end of file + /** Stop sorting players into sois */ + final case class StopPopulation() + + @tailrec + def Populate(buildings : Iterator[(Building, Int)], players : List[Player]) : Unit = { + if(players.nonEmpty && buildings.hasNext) { + val (facility, radius) = buildings.next + val (tenants, remainder) = players.partition(p => Vector3.DistanceSquared(facility.Position.xy, p.Position.xy) < radius) + facility.PlayersInSOI = tenants + Populate(buildings, remainder) + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala index 1d7eb637f..d4d09130e 100644 --- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -13,15 +13,12 @@ import net.psforever.objects.guid.actor.UniqueNumberSystem import net.psforever.objects.guid.selector.RandomSelector import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.inventory.Container -import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition} import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.structures.{Amenity, Building, WarpGate} -import net.psforever.objects.serverobject.terminals.ProximityUnit import net.psforever.objects.serverobject.turret.FacilityTurret import net.psforever.packet.game.PlanetSideGUID import net.psforever.types.{PlanetSideEmpire, Vector3} -import services.Service import services.avatar.AvatarService import services.local.LocalService import services.vehicle.VehicleService @@ -30,9 +27,9 @@ import scala.collection.concurrent.TrieMap import scala.collection.mutable.ListBuffer import scala.collection.immutable.{Map => PairMap} import scala.concurrent.duration._ - import scalax.collection.Graph -import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ +import scalax.collection.GraphPredef._ +import scalax.collection.GraphEdge._ /** * A server object representing the one-landmass planets as well as the individual subterranean caverns.
@@ -413,31 +410,27 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { case silo : ResourceSilo => silo.Actor ! "startup" } - //proximity terminals need to startup - buildings.values - .flatMap(_.Amenities.filter(_.isInstanceOf[ProximityUnit])) - .collect { - case o : PlanetSideServerObject => - o.Actor ! Service.Startup() - } + //some painfields need to look for their closest door buildings.values .flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition])) .collect { case painbox : Painbox => painbox.Actor ! "startup" } + //allocate soi information + soi ! SOI.Build() } private def MakeLattice(): Unit = { Map.LatticeLink.foreach({ case(source, target) => val sourceBuilding = Building(source) match { case Some(building) => building - case _ => throw new NoSuchElementException(s"Can't create lattice link between ${source} ${target}. Source is missing") + case _ => throw new NoSuchElementException(s"Can't create lattice link between $source $target. Source is missing") } val targetBuilding = Building(target) match { case Some(building) => building - case _ => throw new NoSuchElementException(s"Can't create lattice link between ${source} ${target}. Target is missing") + case _ => throw new NoSuchElementException(s"Can't create lattice link between $source $target. Target is missing") } lattice += sourceBuilding~targetBuilding @@ -477,6 +470,24 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { entry } + def StartPlayerManagementSystems() : Unit = { + println(s"start player management for $Id") + soi ! SOI.Populate() +// buildings.values.foreach { _.Amenities.collect { +// case box : Painbox => +// box.Actor ! Painbox.Start() +// } } + } + + def StopPlayerManagementSystems() : Unit = { + println(s"stop player management for $Id") + soi ! SOI.StopPopulation() +// buildings.values.foreach { _.Amenities.collect { +// case box : Painbox => +// box.Actor ! Painbox.Stop() +// } } + } + def Activity : ActorRef = projector def HotSpots : List[HotSpotInfo] = hotspots toList diff --git a/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala index f43ecb266..a77911b7e 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala @@ -3,7 +3,6 @@ package net.psforever.objects.zones import akka.actor.{Actor, ActorRef, Props} import net.psforever.objects.avatar.PlayerControl -import net.psforever.objects.vehicles.VehicleControl import net.psforever.objects.{Avatar, Player} import scala.annotation.tailrec @@ -24,13 +23,18 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player def receive : Receive = { case Zone.Population.Join(avatar) => - PopulationJoin(avatar, playerMap) + if(PopulationJoin(avatar, playerMap) && playerMap.size == 1) { + zone.StartPlayerManagementSystems() + } case Zone.Population.Leave(avatar) => PopulationLeave(avatar, playerMap) match { case None => ; case player @ Some(_) => sender ! Zone.Population.PlayerHasLeft(zone, player) + if(playerMap.isEmpty) { + zone.StopPlayerManagementSystems() + } } case Zone.Population.Spawn(avatar, player) => @@ -42,6 +46,7 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player } else { player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"${player.Name}_${player.GUID.guid}") + player.Zone = zone } case None => sender ! Zone.Population.PlayerCanNotSpawn(zone, player) From d2ef5a76a445bd76247f1d2f931c3a9a446382ae Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 3 Jan 2020 15:29:13 -0500 Subject: [PATCH 2/5] added painbox damage history event --- .../serverobject/painbox/PainboxControl.scala | 3 ++- .../net/psforever/objects/vital/Vitality.scala | 3 +++ .../objects/zones/SphereOfInfluenceActor.scala | 9 ++++++--- .../net/psforever/objects/zones/Zone.scala | 12 ++---------- .../scala/services/avatar/AvatarService.scala | 4 ++-- .../services/avatar/AvatarServiceMessage.scala | 2 +- .../services/avatar/AvatarServiceResponse.scala | 2 +- pslogin/src/main/scala/WorldSessionActor.scala | 17 +++++++++++------ 8 files changed, 28 insertions(+), 24 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala index aad8f4e9a..bf2a8966a 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala @@ -39,6 +39,7 @@ class PainboxControl(painbox: Painbox) extends Actor { //todo: Account for overlapping pain fields //todo: Pain module //todo: REK boosting + val guid = painbox.GUID val owner = painbox.Owner.asInstanceOf[Building] val faction = owner.Faction if(faction != PlanetSideEmpire.NEUTRAL && (!painbox.Definition.HasNearestDoorDependency || (painbox.Definition.HasNearestDoorDependency && nearestDoor.Open.nonEmpty))) { @@ -50,7 +51,7 @@ class PainboxControl(painbox: Painbox) extends Actor { .collect { case p if p.Faction != faction && p.Health > 0 && Vector3.DistanceSquared(p.Position, position) < radius => - events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, damage)) + events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, guid, damage)) } } diff --git a/common/src/main/scala/net/psforever/objects/vital/Vitality.scala b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala index 17a862bce..5cf22e3a2 100644 --- a/common/src/main/scala/net/psforever/objects/vital/Vitality.scala +++ b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala @@ -4,6 +4,7 @@ package net.psforever.objects.vital import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry, VehicleSource} import net.psforever.objects.definition.KitDefinition +import net.psforever.objects.serverobject.painbox.Painbox import net.psforever.objects.serverobject.terminals.TerminalDefinition import net.psforever.objects.vital.resolution.ResolutionCalculations import net.psforever.types.{ExoSuitType, ImplantType} @@ -35,6 +36,8 @@ final case class VehicleShieldCharge(target : VehicleSource, amount : Int) exten final case class DamageFromProjectile(data : ResolvedProjectile) extends DamagingActivity(data.target) +final case class DamageFromPainbox(target : PlayerSource, painbox : Painbox, damage : Int) extends DamagingActivity(target) + final case class PlayerSuicide(target : PlayerSource) extends DamagingActivity(target) /** diff --git a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala index 844c643b3..164ae7c97 100644 --- a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala @@ -26,7 +26,7 @@ class SphereOfInfluenceActor(zone: Zone) extends Actor { case SOI.Populate() => UpdateSOI() - case SOI.StopPopulation() => + case SOI.Stop() => context.become(Stopped) populateTick.cancel sois.foreach { case (facility, _) => facility.PlayersInSOI = Nil } @@ -35,7 +35,7 @@ class SphereOfInfluenceActor(zone: Zone) extends Actor { } def Stopped : Receive = Build.orElse { - case SOI.Populate() => + case SOI.Start() => context.become(Running) UpdateSOI() @@ -53,6 +53,7 @@ class SphereOfInfluenceActor(zone: Zone) extends Actor { def UpdateSOI(): Unit = { SOI.Populate(sois.iterator, zone.LivePlayers) + populateTick.cancel populateTick = context.system.scheduler.scheduleOnce(5 seconds, self, SOI.Populate()) } } @@ -63,7 +64,9 @@ object SOI { /** Populate the list of players within a SOI **/ final case class Populate() /** Stop sorting players into sois */ - final case class StopPopulation() + final case class Start() + /** Stop sorting players into sois */ + final case class Stop() @tailrec def Populate(buildings : Iterator[(Building, Int)], players : List[Player]) : Unit = { diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala index d4d09130e..44884dcc7 100644 --- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -472,20 +472,12 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { def StartPlayerManagementSystems() : Unit = { println(s"start player management for $Id") - soi ! SOI.Populate() -// buildings.values.foreach { _.Amenities.collect { -// case box : Painbox => -// box.Actor ! Painbox.Start() -// } } + soi ! SOI.Start() } def StopPlayerManagementSystems() : Unit = { println(s"stop player management for $Id") - soi ! SOI.StopPopulation() -// buildings.values.foreach { _.Amenities.collect { -// case box : Painbox => -// box.Actor ! Painbox.Stop() -// } } + soi ! SOI.Stop() } def Activity : ActorRef = projector diff --git a/common/src/main/scala/services/avatar/AvatarService.scala b/common/src/main/scala/services/avatar/AvatarService.scala index e7b724a79..2a5643cfb 100644 --- a/common/src/main/scala/services/avatar/AvatarService.scala +++ b/common/src/main/scala/services/avatar/AvatarService.scala @@ -65,9 +65,9 @@ class AvatarService(zone : Zone) extends Actor { AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer()) ) - case AvatarAction.EnvironmentalDamage(player_guid, amount) => + case AvatarAction.EnvironmentalDamage(player_guid, source_guid, amount) => AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EnvironmentalDamage(player_guid, amount)) + AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EnvironmentalDamage(player_guid, source_guid, amount)) ) case AvatarAction.Damage(player_guid, target, resolution_function) => AvatarEvents.publish( diff --git a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala index a3e219aa8..904423dc7 100644 --- a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala +++ b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala @@ -31,7 +31,7 @@ object AvatarAction { final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action - final case class EnvironmentalDamage(player_guid : PlanetSideGUID, amount: Int) extends Action + final case class EnvironmentalDamage(player_guid : PlanetSideGUID, source_guid : PlanetSideGUID, amount: Int) extends Action final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : ResolutionCalculations.Output) extends Action final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action diff --git a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala index 2140dbf7b..0c589822a 100644 --- a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala +++ b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala @@ -25,7 +25,7 @@ object AvatarResponse { final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response final case class ConcealPlayer() extends Response - final case class EnvironmentalDamage(target : PlanetSideGUID, amount : Int) extends Response + final case class EnvironmentalDamage(target : PlanetSideGUID, source_guid : PlanetSideGUID, amount : Int) extends Response final case class DamageResolution(target : Player, resolution_function : ResolutionCalculations.Output) extends Response final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Response final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index f7e5c9d37..2a3b58451 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -33,6 +33,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} +import net.psforever.objects.serverobject.painbox.Painbox import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate} import net.psforever.objects.serverobject.terminals._ @@ -1212,11 +1213,18 @@ class WorldSessionActor extends Actor case AvatarResponse.ConcealPlayer() => sendResponse(GenericObjectActionMessage(guid, 9)) - case AvatarResponse.EnvironmentalDamage(target, amount) => - if(player.isAlive && amount != 0) { + case AvatarResponse.EnvironmentalDamage(target, source, amount) => + if(player.isAlive && amount > 0) { + val playerGUID = player.GUID val armor = player.Armor val capacitor = player.Capacitor val originalHealth = player.Health + //history + continent.GUID(source) match { + case Some(obj : Painbox) => + player.History(DamageFromPainbox(PlayerSource(player), obj, amount)) + case _ => ; + } player.Health = originalHealth - amount sendResponse(PlanetsideAttributeMessage(target, 0, player.Health)) continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(target, 0, player.Health)) @@ -1224,9 +1232,6 @@ class WorldSessionActor extends Actor if(player.Health == 0 && player.isAlive) { KillPlayer(player) } - else { - //todo: History? - } } case AvatarResponse.DamageResolution(target, resolution_function) => @@ -3274,7 +3279,7 @@ class WorldSessionActor extends Actor import net.psforever.objects.GlobalDefinitions._ import net.psforever.types.CertificationType._ - val faction = PlanetSideEmpire.VS + val faction = PlanetSideEmpire.TR val avatar = new Avatar(41605313L+sessionId, s"TestCharacter$sessionId", faction, CharacterGender.Female, 41, CharacterVoice.Voice1) avatar.Certifications += StandardAssault avatar.Certifications += MediumAssault From 837e9cb2ff73a51c626c38f3d03495ca5f6b496b Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 3 Jan 2020 20:24:44 -0500 Subject: [PATCH 3/5] how painbox doors work; moved constant chat system subscriptions; swapped around medical terminal coordinates on home3 --- .../serverobject/painbox/PainboxControl.scala | 34 +++++++++++++------ .../zones/SphereOfInfluenceActor.scala | 6 ++++ .../src/main/scala/WorldSessionActor.scala | 28 ++++++++------- pslogin/src/main/scala/zonemaps/Map13.scala | 4 +-- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala index bf2a8966a..36230615f 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala @@ -11,19 +11,31 @@ import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global class PainboxControl(painbox: Painbox) extends Actor { - //private[this] val log = org.log4s.getLogger(s"Painbox") - private var painboxTick: Cancellable = DefaultCancellable.obj - private var nearestDoor : Door = null + private[this] val log = org.log4s.getLogger(s"Painbox") + var painboxTick: Cancellable = DefaultCancellable.obj + var nearestDoor : Option[Door] = None def receive : Receive = { case "startup" => - painbox.Owner match { - case obj : Building => - nearestDoor = obj.Amenities - .collect { case door : Door => door } - .minBy(door => Vector3.DistanceSquared(painbox.Position, door.Position)) - context.become(Stopped) - case _ => ; + if(painbox.Definition.HasNearestDoorDependency) { + (painbox.Owner match { + case obj : Building => + obj.Amenities + .collect { case door : Door => door } + .sortBy(door => Vector3.DistanceSquared(painbox.Position, door.Position)) + .headOption + case _ => + None + }) match { + case door @ Some(_) => + nearestDoor = door + context.become(Stopped) + case _ => + log.error(s"object #${painbox.GUID.guid} can not find a door that it needed") + } + } + else { + context.become(Stopped) } case _ => ; @@ -42,7 +54,7 @@ class PainboxControl(painbox: Painbox) extends Actor { val guid = painbox.GUID val owner = painbox.Owner.asInstanceOf[Building] val faction = owner.Faction - if(faction != PlanetSideEmpire.NEUTRAL && (!painbox.Definition.HasNearestDoorDependency || (painbox.Definition.HasNearestDoorDependency && nearestDoor.Open.nonEmpty))) { + if(faction != PlanetSideEmpire.NEUTRAL && (nearestDoor match { case Some(door) => door.Open.nonEmpty; case _ => true })) { val events = owner.Zone.AvatarEvents val damage = painbox.Definition.Damage val radius = painbox.Definition.Radius * painbox.Definition.Radius diff --git a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala index 164ae7c97..8777e5217 100644 --- a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala @@ -68,6 +68,12 @@ object SOI { /** Stop sorting players into sois */ final case class Stop() + /** + * Recursively populate each facility's sphere of influence with players. + * @param buildings an iterator of buildings and the radius of its sphere of influence + * @param players a list of players to allocate; + * the list gets shorter as each building is allocated + */ @tailrec def Populate(buildings : Iterator[(Building, Int)], players : List[Player]) : Unit = { if(players.nonEmpty && buildings.hasNext) { diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 2a3b58451..5c283eeb2 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -121,7 +121,9 @@ class WorldSessionActor extends Actor var drawDeloyableIcon : PlanetSideGameObject with Deployable => Unit = RedrawDeployableIcons var updateSquad : () => Unit = NoSquadUpdates var recentTeleportAttempt : Long = 0 - var lastTerminalOrderFulfillment : Boolean = true /** + var lastTerminalOrderFulfillment : Boolean = true + var shiftPosition : Option[Vector3] = None + /** * used during zone transfers to maintain reference to seated vehicle (which does not yet exist in the new zone) * used during intrazone gate transfers, but not in a way distinct from prior zone transfer procedures * should only be set during the transient period when moving between one spawn point and the next @@ -1033,10 +1035,19 @@ class WorldSessionActor extends Actor sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list sendResponse(FriendsResponse(FriendAction.InitializeFriendList, 0, true, true, Nil)) sendResponse(FriendsResponse(FriendAction.InitializeIgnoreList, 0, true, true, Nil)) + //the following subscriptions last until character switch/logout + chatService ! Service.Join("local") + chatService ! Service.Join("squad") + chatService ! Service.Join("voice") + chatService ! Service.Join("tell") + chatService ! Service.Join("broadcast") + chatService ! Service.Join("note") + chatService ! Service.Join("gm") galaxyService ! Service.Join("galaxy") //for galaxy-wide messages galaxyService ! Service.Join(s"${avatar.faction}") //for hotspots squadService ! Service.Join(s"${avatar.faction}") //channel will be player.Faction squadService ! Service.Join(s"${avatar.CharId}") //channel will be player.CharId (in order to work with packets) + //go home cluster ! InterstellarCluster.GetWorld("home3") case InterstellarCluster.GiveWorld(zoneId, zone) => @@ -3061,7 +3072,8 @@ class WorldSessionActor extends Actor sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 75, 0)) sendResponse(SetCurrentAvatarMessage(guid, 0, 0)) sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on //TODO once per respawn? - sendResponse(PlayerStateShiftMessage(ShiftState(1, tplayer.Position, tplayer.Orientation.z))) + sendResponse(PlayerStateShiftMessage(ShiftState(1, shiftPosition.getOrElse(tplayer.Position), tplayer.Orientation.z))) + shiftPosition = None if(spectator) { sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None)) } @@ -3279,7 +3291,7 @@ class WorldSessionActor extends Actor import net.psforever.objects.GlobalDefinitions._ import net.psforever.types.CertificationType._ - val faction = PlanetSideEmpire.TR + val faction = PlanetSideEmpire.VS val avatar = new Avatar(41605313L+sessionId, s"TestCharacter$sessionId", faction, CharacterGender.Female, 41, CharacterVoice.Voice1) avatar.Certifications += StandardAssault avatar.Certifications += MediumAssault @@ -3669,15 +3681,6 @@ class WorldSessionActor extends Actor } } continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent)) - - chatService ! Service.Join("local") - chatService ! Service.Join("squad") - chatService ! Service.Join("voice") - chatService ! Service.Join("tell") - chatService ! Service.Join("broadcast") - chatService ! Service.Join("note") - chatService ! Service.Join("gm") - self ! SetCurrentAvatar(player) case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, jump_thrust, is_cloaking, unk5, unk6) => @@ -9098,6 +9101,7 @@ class WorldSessionActor extends Actor val respawnTimeMillis = respawnTime * 1000 //ms deadState = DeadState.RespawnTime sendResponse(AvatarDeadStateMessage(DeadState.RespawnTime, respawnTimeMillis, respawnTimeMillis, Vector3.Zero, player.Faction, true)) + shiftPosition = Some(pos) val (target, msg) = if(backpack) { //if the player is dead, he is handled as dead infantry, even if he died in a vehicle //new player is spawning val newPlayer = RespawnClone(player) diff --git a/pslogin/src/main/scala/zonemaps/Map13.scala b/pslogin/src/main/scala/zonemaps/Map13.scala index a80f0511d..4240232bb 100644 --- a/pslogin/src/main/scala/zonemaps/Map13.scala +++ b/pslogin/src/main/scala/zonemaps/Map13.scala @@ -201,8 +201,8 @@ object Map13 { LocalObject(868, Terminal.Constructor(Vector3(3760.041f, 2809.603f, 94.65012f), order_terminal), owning_building_guid = 2) LocalObject(869, Terminal.Constructor(Vector3(3763.91f, 2806.405f, 94.65012f), order_terminal), owning_building_guid = 2) LocalObject(870, Terminal.Constructor(Vector3(3763.931f, 2809.603f, 94.65012f), order_terminal), owning_building_guid = 2) - LocalObject(778, ProximityTerminal.Constructor(Vector3(3617.307f, 2830.855f, 93.34412f), medical_terminal), owning_building_guid = 2) - LocalObject(779, ProximityTerminal.Constructor(Vector3(3617.369f, 2785.151f, 93.34412f), medical_terminal), owning_building_guid = 2) + LocalObject(778, ProximityTerminal.Constructor(Vector3(3617.369f, 2785.151f, 93.34412f), medical_terminal), owning_building_guid = 2) + LocalObject(779, ProximityTerminal.Constructor(Vector3(3617.307f, 2830.855f, 93.34412f), medical_terminal), owning_building_guid = 2) LocalObject(780, ProximityTerminal.Constructor(Vector3(3758.667f, 2785.151f, 93.34412f), medical_terminal), owning_building_guid = 2) LocalObject(781, ProximityTerminal.Constructor(Vector3(3758.689f, 2830.855f, 93.34412f), medical_terminal), owning_building_guid = 2) LocalObject(522, ImplantTerminalMech.Constructor, owning_building_guid = 2) From 6e678373d28638518c67261f95c2e952d47f9cd5 Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 3 Jan 2020 23:01:31 -0500 Subject: [PATCH 4/5] removed developer messages for soi management --- common/src/main/scala/net/psforever/objects/zones/Zone.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala index 44884dcc7..157085ba4 100644 --- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -471,12 +471,10 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { } def StartPlayerManagementSystems() : Unit = { - println(s"start player management for $Id") soi ! SOI.Start() } def StopPlayerManagementSystems() : Unit = { - println(s"stop player management for $Id") soi ! SOI.Stop() } From d7ee2be46b5c492a0a1342518c4efff4495af217 Mon Sep 17 00:00:00 2001 From: FateJH Date: Sat, 4 Jan 2020 00:39:59 -0500 Subject: [PATCH 5/5] condition to restrict soi selection if no soi information was constructed --- .../net/psforever/objects/zones/SphereOfInfluenceActor.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala index 8777e5217..d32f0f830 100644 --- a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala @@ -35,7 +35,7 @@ class SphereOfInfluenceActor(zone: Zone) extends Actor { } def Stopped : Receive = Build.orElse { - case SOI.Start() => + case SOI.Start() if sois.nonEmpty => context.become(Running) UpdateSOI()