mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-15 16:34:39 +00:00
sphere of influence is now activated by zone denizens; painboxes are activated by sphere of influence denizens
This commit is contained in:
parent
a5f1638fd6
commit
4261693ae1
7 changed files with 121 additions and 54 deletions
|
|
@ -10,7 +10,9 @@ class Painbox(tdef : PainboxDefinition) extends Amenity {
|
||||||
}
|
}
|
||||||
|
|
||||||
object Painbox {
|
object Painbox {
|
||||||
|
final case class Start()
|
||||||
final case class Tick()
|
final case class Tick()
|
||||||
|
final case class Stop()
|
||||||
|
|
||||||
def apply(tdef : PainboxDefinition) : Painbox = {
|
def apply(tdef : PainboxDefinition) : Painbox = {
|
||||||
new Painbox(tdef)
|
new Painbox(tdef)
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,19 @@ class PainboxControl(painbox: Painbox) extends Actor {
|
||||||
nearestDoor = obj.Amenities
|
nearestDoor = obj.Amenities
|
||||||
.collect { case door : Door => door }
|
.collect { case door : Door => door }
|
||||||
.minBy(door => Vector3.DistanceSquared(painbox.Position, door.Position))
|
.minBy(door => Vector3.DistanceSquared(painbox.Position, door.Position))
|
||||||
painboxTick = context.system.scheduler.schedule(0 seconds,1 second, self, Painbox.Tick())
|
context.become(Stopped)
|
||||||
context.become(Processing)
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
def Processing : Receive = {
|
def Running : Receive = {
|
||||||
|
case Painbox.Stop() =>
|
||||||
|
context.become(Stopped)
|
||||||
|
painboxTick.cancel
|
||||||
|
painboxTick = DefaultCancellable.obj
|
||||||
|
|
||||||
case Painbox.Tick() =>
|
case Painbox.Tick() =>
|
||||||
//todo: Account for overlapping pain fields
|
//todo: Account for overlapping pain fields
|
||||||
//todo: Pain module
|
//todo: Pain module
|
||||||
|
|
@ -49,5 +53,16 @@ class PainboxControl(painbox: Painbox) extends Actor {
|
||||||
events ! AvatarServiceMessage(p.Name, AvatarAction.EnvironmentalDamage(p.GUID, damage))
|
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 _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import akka.actor.{ActorContext, ActorRef}
|
||||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.serverobject.hackable.Hackable
|
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.resourcesilo.ResourceSilo
|
||||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
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[Player] = playersInSOI
|
||||||
|
|
||||||
def PlayersInSOI_=(list : List[Player]) : List[Player] = {
|
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 = list
|
||||||
playersInSOI
|
playersInSOI
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import net.psforever.objects._
|
||||||
import net.psforever.objects.serverobject.CommonMessages
|
import net.psforever.objects.serverobject.CommonMessages
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
import net.psforever.objects.serverobject.hackable.HackableBehavior
|
import net.psforever.objects.serverobject.hackable.HackableBehavior
|
||||||
import services.Service
|
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -27,17 +26,7 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor
|
||||||
|
|
||||||
def TerminalObject : Terminal with ProximityUnit = term
|
def TerminalObject : Terminal with ProximityUnit = term
|
||||||
|
|
||||||
def receive : Receive = Start
|
def receive : Receive = checkBehavior
|
||||||
|
|
||||||
def Start : Receive = checkBehavior
|
|
||||||
.orElse {
|
|
||||||
case Service.Startup() =>
|
|
||||||
context.become(Run)
|
|
||||||
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
def Run : Receive = checkBehavior
|
|
||||||
.orElse(hackableBehavior)
|
.orElse(hackableBehavior)
|
||||||
.orElse {
|
.orElse {
|
||||||
case CommonMessages.Use(_, Some(target : PlanetSideGameObject)) =>
|
case CommonMessages.Use(_, Some(target : PlanetSideGameObject)) =>
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,77 @@
|
||||||
package net.psforever.objects.zones;
|
package net.psforever.objects.zones
|
||||||
|
|
||||||
import akka.actor.{Actor, Cancellable}
|
import akka.actor.{Actor, Cancellable}
|
||||||
|
import net.psforever.objects.{DefaultCancellable, Player}
|
||||||
import net.psforever.objects.definition.ObjectDefinition
|
import net.psforever.objects.definition.ObjectDefinition
|
||||||
import net.psforever.objects.serverobject.structures.{Building, SphereOfInfluence}
|
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.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class SphereOfInfluenceActor(zone: Zone) extends Actor {
|
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())
|
def receive : Receive = Stopped
|
||||||
private[this] val log = org.log4s.getLogger(s"${zone.Id.capitalize}-SphereOfInfluenceActor")
|
|
||||||
|
|
||||||
def Established : Receive = {
|
def Build : Receive = {
|
||||||
|
case SOI.Build() =>
|
||||||
|
BuildSOI()
|
||||||
|
}
|
||||||
|
|
||||||
|
def Running : Receive = Build.orElse {
|
||||||
case SOI.Populate() =>
|
case SOI.Populate() =>
|
||||||
UpdateSOI()
|
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 = {
|
def UpdateSOI(): Unit = {
|
||||||
val players = zone.LivePlayers
|
SOI.Populate(sois.iterator, 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 _ => ;
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
populateTick = context.system.scheduler.scheduleOnce(5 seconds, self, SOI.Populate())
|
populateTick = context.system.scheduler.scheduleOnce(5 seconds, self, SOI.Populate())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object SOI {
|
object SOI {
|
||||||
|
/** Rebuild the list of facility SOI data **/
|
||||||
|
final case class Build()
|
||||||
/** Populate the list of players within a SOI **/
|
/** Populate the list of players within a SOI **/
|
||||||
final case class Populate()
|
final case class Populate()
|
||||||
|
/** 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,15 +13,12 @@ import net.psforever.objects.guid.actor.UniqueNumberSystem
|
||||||
import net.psforever.objects.guid.selector.RandomSelector
|
import net.psforever.objects.guid.selector.RandomSelector
|
||||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||||
import net.psforever.objects.inventory.Container
|
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.painbox.{Painbox, PainboxDefinition}
|
||||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||||
import net.psforever.objects.serverobject.structures.{Amenity, Building, WarpGate}
|
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.objects.serverobject.turret.FacilityTurret
|
||||||
import net.psforever.packet.game.PlanetSideGUID
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
import services.Service
|
|
||||||
import services.avatar.AvatarService
|
import services.avatar.AvatarService
|
||||||
import services.local.LocalService
|
import services.local.LocalService
|
||||||
import services.vehicle.VehicleService
|
import services.vehicle.VehicleService
|
||||||
|
|
@ -30,9 +27,9 @@ import scala.collection.concurrent.TrieMap
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
import scala.collection.immutable.{Map => PairMap}
|
import scala.collection.immutable.{Map => PairMap}
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import scalax.collection.Graph
|
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.<br>
|
* A server object representing the one-landmass planets as well as the individual subterranean caverns.<br>
|
||||||
|
|
@ -413,31 +410,27 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
case silo : ResourceSilo =>
|
case silo : ResourceSilo =>
|
||||||
silo.Actor ! "startup"
|
silo.Actor ! "startup"
|
||||||
}
|
}
|
||||||
//proximity terminals need to startup
|
//some painfields need to look for their closest door
|
||||||
buildings.values
|
|
||||||
.flatMap(_.Amenities.filter(_.isInstanceOf[ProximityUnit]))
|
|
||||||
.collect {
|
|
||||||
case o : PlanetSideServerObject =>
|
|
||||||
o.Actor ! Service.Startup()
|
|
||||||
}
|
|
||||||
buildings.values
|
buildings.values
|
||||||
.flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition]))
|
.flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition]))
|
||||||
.collect {
|
.collect {
|
||||||
case painbox : Painbox =>
|
case painbox : Painbox =>
|
||||||
painbox.Actor ! "startup"
|
painbox.Actor ! "startup"
|
||||||
}
|
}
|
||||||
|
//allocate soi information
|
||||||
|
soi ! SOI.Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def MakeLattice(): Unit = {
|
private def MakeLattice(): Unit = {
|
||||||
Map.LatticeLink.foreach({ case(source, target) =>
|
Map.LatticeLink.foreach({ case(source, target) =>
|
||||||
val sourceBuilding = Building(source) match {
|
val sourceBuilding = Building(source) match {
|
||||||
case Some(building) => building
|
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 {
|
val targetBuilding = Building(target) match {
|
||||||
case Some(building) => building
|
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
|
lattice += sourceBuilding~targetBuilding
|
||||||
|
|
@ -477,6 +470,24 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
entry
|
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 Activity : ActorRef = projector
|
||||||
|
|
||||||
def HotSpots : List[HotSpotInfo] = hotspots toList
|
def HotSpots : List[HotSpotInfo] = hotspots toList
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package net.psforever.objects.zones
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
import net.psforever.objects.avatar.PlayerControl
|
import net.psforever.objects.avatar.PlayerControl
|
||||||
import net.psforever.objects.vehicles.VehicleControl
|
|
||||||
import net.psforever.objects.{Avatar, Player}
|
import net.psforever.objects.{Avatar, Player}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
@ -24,13 +23,18 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player
|
||||||
|
|
||||||
def receive : Receive = {
|
def receive : Receive = {
|
||||||
case Zone.Population.Join(avatar) =>
|
case Zone.Population.Join(avatar) =>
|
||||||
PopulationJoin(avatar, playerMap)
|
if(PopulationJoin(avatar, playerMap) && playerMap.size == 1) {
|
||||||
|
zone.StartPlayerManagementSystems()
|
||||||
|
}
|
||||||
|
|
||||||
case Zone.Population.Leave(avatar) =>
|
case Zone.Population.Leave(avatar) =>
|
||||||
PopulationLeave(avatar, playerMap) match {
|
PopulationLeave(avatar, playerMap) match {
|
||||||
case None => ;
|
case None => ;
|
||||||
case player @ Some(_) =>
|
case player @ Some(_) =>
|
||||||
sender ! Zone.Population.PlayerHasLeft(zone, player)
|
sender ! Zone.Population.PlayerHasLeft(zone, player)
|
||||||
|
if(playerMap.isEmpty) {
|
||||||
|
zone.StopPlayerManagementSystems()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case Zone.Population.Spawn(avatar, player) =>
|
case Zone.Population.Spawn(avatar, player) =>
|
||||||
|
|
@ -42,6 +46,7 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"${player.Name}_${player.GUID.guid}")
|
player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"${player.Name}_${player.GUID.guid}")
|
||||||
|
player.Zone = zone
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
sender ! Zone.Population.PlayerCanNotSpawn(zone, player)
|
sender ! Zone.Population.PlayerCanNotSpawn(zone, player)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue