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..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,33 +11,50 @@ 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)) - painboxTick = context.system.scheduler.schedule(0 seconds,1 second, self, Painbox.Tick()) - context.become(Processing) - 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 _ => ; } - 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 //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))) { + 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 @@ -46,8 +63,19 @@ 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)) } } + + 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/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 a34ed5403..d32f0f830 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,86 @@ -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.Stop() => + context.become(Stopped) + populateTick.cancel + sois.foreach { case (facility, _) => facility.PlayersInSOI = Nil } + + case _ => ; + } + + def Stopped : Receive = Build.orElse { + case SOI.Start() if sois.nonEmpty => + 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.cancel 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 Start() + /** 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) { + 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..157085ba4 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,14 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { entry } + def StartPlayerManagementSystems() : Unit = { + soi ! SOI.Start() + } + + def StopPlayerManagementSystems() : Unit = { + soi ! SOI.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) 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..5c283eeb2 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._ @@ -120,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 @@ -1032,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) => @@ -1212,11 +1224,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 +1243,6 @@ class WorldSessionActor extends Actor if(player.Health == 0 && player.isAlive) { KillPlayer(player) } - else { - //todo: History? - } } case AvatarResponse.DamageResolution(target, resolution_function) => @@ -3056,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)) } @@ -3664,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) => @@ -9093,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)