diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala index bbbb3a982..4575dac59 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -3025,6 +3025,7 @@ class ZoningOperations( physSpawnPoint: Option[SpawnPoint] ): Unit = { log.info(s"${player.Name} will load in zone $zoneId at position $pos in $respawnTime") + player.resetInteractions() respawnTimer.cancel() reviveTimer.cancel() deadState = DeadState.RespawnTime diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala index c0023ebb6..3621433bd 100644 --- a/src/main/scala/net/psforever/objects/Player.scala +++ b/src/main/scala/net/psforever/objects/Player.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects -import net.psforever.objects.avatar.interaction.{TriggerOnPlayerRule, WithEntrance, WithGantry, WithLava, WithWater} +import net.psforever.objects.avatar.interaction.{InteractWithSOI, TriggerOnPlayerRule, WithEntrance, WithGantry, WithLava, WithWater} import net.psforever.objects.avatar.{Avatar, LoadoutManager, SpecialCarry} import net.psforever.objects.ballistics.InteractWithRadiationClouds import net.psforever.objects.ce.{Deployable, InteractWithMines, InteractWithTurrets} @@ -40,6 +40,7 @@ class Player(var avatar: Avatar) with InteriorAwareFromInteraction with AuraContainer with MountableEntity { + interaction(new InteractWithSOI()) interaction(environment.interaction.InteractWithEnvironment(Seq( new WithEntrance(), new WithWater(avatar.name), diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/InteractWithSOI.scala b/src/main/scala/net/psforever/objects/avatar/interaction/InteractWithSOI.scala new file mode 100644 index 000000000..df880d7af --- /dev/null +++ b/src/main/scala/net/psforever/objects/avatar/interaction/InteractWithSOI.scala @@ -0,0 +1,63 @@ +// Copyright (c) 2025 PSForever +package net.psforever.objects.avatar.interaction + +import net.psforever.objects.Player +import net.psforever.objects.serverobject.structures.Building +import net.psforever.objects.zones.{InteractsWithZone, ZoneInteraction, ZoneInteractionType} +import net.psforever.objects.zones.blockmap.SectorPopulation +import net.psforever.types.Vector3 + +case object SOIInteraction extends ZoneInteractionType + +/** + * na + */ +class InteractWithSOI() + extends ZoneInteraction { + + private var skipTargetCounter: Int = 0 + private var occupiedSOIs: List[(String, Building)] = List() + + def Type: ZoneInteractionType = SOIInteraction + + def range: Float = 0f + + /** + * na + * @param sector the portion of the block map being tested + * @param target the fixed element in this test + */ + def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = { + if (skipTargetCounter < 4) { + skipTargetCounter += 1 + } else { + skipTargetCounter = 0 + val buildings = sector.buildingList + if (occupiedSOIs.nonEmpty || buildings.nonEmpty) { + val targetZone = target.Zone + val targetPosition = target.Position + val targetAsPlayer = target.asInstanceOf[Player] + val key = targetAsPlayer.CharId + val (ongoingOccupancy, nowUnoccupied) = occupiedSOIs.partition { case (_, b) => + targetZone == b.Zone && Vector3.DistanceSquared(targetPosition, b.Position) < math.pow(b.Definition.SOIRadius, 2).toFloat + } + val (_, targetBuildingsOnly) = ongoingOccupancy.unzip + val (_, newOccupancy) = buildings + .filter { b => + Vector3.DistanceSquared(b.Position, targetPosition) < math.pow(b.Definition.SOIRadius, 2) + } + .partition { b => targetBuildingsOnly.exists(_.Name.equals(b.Name)) } + occupiedSOIs = ongoingOccupancy ++ newOccupancy.map { building => (building.Name, building) } + nowUnoccupied.map(_._2).foreach { _.RemovePlayersFromSOI(key) } + newOccupancy.foreach { _.AddPlayersInSOI(key, targetAsPlayer) } + } + } + } + + def resetInteraction(target: InteractsWithZone): Unit = { + skipTargetCounter = 0 + val charId = target.asInstanceOf[Player].CharId + occupiedSOIs.map(_._2).foreach { _.RemovePlayersFromSOI(charId) } + occupiedSOIs = List() + } +} diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala index a7863ab56..51a6637fc 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -20,6 +20,8 @@ import net.psforever.objects.serverobject.structures.participation.{MajorFacilit import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.util.Config +import scala.collection.mutable + class Building( private val name: String, private val building_guid: Int, @@ -31,7 +33,7 @@ class Building( with BlockMapEntity { private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL - private var playersInSOI: List[Player] = List.empty + private var playersInSOI: mutable.HashMap[Long, Player] = mutable.HashMap[Long, Player]() private var forceDomeActive: Boolean = false private var participationFunc: ParticipationLogic = NoParticipation var virusId: Long = 8 // 8 default = no virus @@ -72,9 +74,10 @@ class Building( Faction } - def PlayersInSOI: List[Player] = playersInSOI + def PlayersInSOI: List[Player] = playersInSOI.values.toList def PlayersInSOI_=(list: List[Player]): List[Player] = { + //todo if (playersInSOI.isEmpty && list.nonEmpty) { Amenities.collect { case box: Painbox => @@ -86,9 +89,29 @@ class Building( box.Actor ! Painbox.Stop() } } - playersInSOI = list + list.foreach { player => + playersInSOI.put(player.CharId, player) + } participationFunc.TryUpdate() - playersInSOI + PlayersInSOI + } + + def AddPlayersInSOI(player: Player): List[Player] = { + AddPlayersInSOI(player.CharId, player) + } + def AddPlayersInSOI(key: Long, player: Player): List[Player] = { + playersInSOI.put(key, player) + participationFunc.TryUpdate() + PlayersInSOI + } + + def RemovePlayersFromSOI(player: Player): List[Player] = { + RemovePlayersFromSOI(player.CharId) + } + def RemovePlayersFromSOI(key: Long): List[Player] = { + playersInSOI.remove(key) + participationFunc.TryUpdate() + PlayersInSOI } // Get all lattice neighbours diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 1caf87d72..525f9a4ca 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -91,7 +91,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { var actor: typed.ActorRef[ZoneActor.Command] = Default.typed.Actor /** Actor that handles SOI related functionality, for example if a player is in an SOI */ - private var soi = Default.Actor + //private var soi = Default.Actor /** The basic support structure for the globally unique number system used by this `Zone`. */ private var guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(max = 65536)) @@ -520,11 +520,11 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { } def StartPlayerManagementSystems(): Unit = { - soi ! SOI.Start() + //soi ! SOI.Start() } def StopPlayerManagementSystems(): Unit = { - soi ! SOI.Stop() + //soi ! SOI.Stop() } def Activity: ActorRef = projector @@ -1536,7 +1536,7 @@ object Zone { Props(classOf[ZoneHotSpotDisplay], zone, zone.hotspots, 15 seconds, zone.hotspotHistory, 60 seconds), s"$id-hotspots" ) - zone.soi = context.actorOf(Props(classOf[SphereOfInfluenceActor], zone), s"$id-soi") + //zone.soi = context.actorOf(Props(classOf[SphereOfInfluenceActor], zone), s"$id-soi") zone.avatarEvents = context.actorOf(Props(classOf[AvatarService], zone), s"$id-avatar-events") zone.localEvents = context.actorOf(Props(classOf[LocalService], zone), s"$id-local-events") @@ -1647,7 +1647,7 @@ object Zone { obj.Actor ! Service.Startup() } //allocate soi information - zone.soi ! SOI.Build() + //zone.soi ! SOI.Build() } private def MakeLattice(zone: Zone): Unit = { diff --git a/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala b/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala index f7480a8a6..4780e4adc 100644 --- a/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala @@ -110,6 +110,7 @@ class ZonePopulationActor(zone: Zone, playerMap: TrieMap[Int, Option[Player]], c case Zone.Corpse.Remove(player) => if (CorpseRemove(player, corpseList)) { PlayerLeave(player) + player.allowInteraction = false zone.actor ! ZoneActor.RemoveFromBlockMap(player) }