mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
Transfer base benefits via lattice (#307)
* Fix missing ObjectType on LocalBuildings, causing pain fields to stop working due to no SOI radius being set * Fix damage logic for pain fields that don't rely on the nearest door * Transfer base benefits via lattice * Missed two Building.SendMapUpdate -> Building.TriggerZoneMapUpdate * Fix Building tests
This commit is contained in:
parent
73298a2e06
commit
4d742e9fee
|
|
@ -69,12 +69,13 @@ class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with Factio
|
|||
self ! ResourceSilo.LowNtuWarning(enabled = true)
|
||||
}
|
||||
|
||||
val building = resourceSilo.Owner
|
||||
val building = resourceSilo.Owner.asInstanceOf[Building]
|
||||
val zone = building.Zone
|
||||
if(resourceSilo.ChargeLevel == 0 && siloChargeBeforeChange > 0) {
|
||||
// Oops, someone let the base run out of power. Shut it all down.
|
||||
//todo: Make base neutral if silo hits zero NTU
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.PlanetsideAttribute(building.GUID, 48, 1))
|
||||
building.TriggerZoneMapUpdate()
|
||||
} else if (siloChargeBeforeChange == 0 && resourceSilo.ChargeLevel > 0) {
|
||||
// Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal.
|
||||
//todo: Check generator is online before starting up
|
||||
|
|
@ -82,6 +83,7 @@ class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with Factio
|
|||
zone.Id,
|
||||
AvatarAction.PlanetsideAttribute(building.GUID, 48, 0)
|
||||
)
|
||||
building.TriggerZoneMapUpdate()
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.structures
|
|||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import akka.actor.{ActorContext, ActorRef}
|
||||
import net.psforever.objects.{GlobalDefinitions, Player}
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
|
|
@ -13,6 +13,7 @@ import net.psforever.objects.serverobject.tube.SpawnTube
|
|||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import scalax.collection.{Graph, GraphEdge}
|
||||
|
||||
class Building(private val name: String,
|
||||
private val building_guid : Int,
|
||||
|
|
@ -40,6 +41,7 @@ class Building(private val name: String,
|
|||
|
||||
override def Faction_=(fac : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = {
|
||||
faction = fac
|
||||
TriggerZoneMapUpdate()
|
||||
Faction
|
||||
}
|
||||
|
||||
|
|
@ -66,6 +68,20 @@ class Building(private val name: String,
|
|||
}
|
||||
}
|
||||
|
||||
def NtuLevel : Int = {
|
||||
//if we have a silo, get the NTU level
|
||||
Amenities.find(_.Definition == GlobalDefinitions.resource_silo) match {
|
||||
case Some(obj: ResourceSilo) =>
|
||||
obj.CapacitorDisplay.toInt
|
||||
case _ => //we have no silo; we have unlimited power
|
||||
10
|
||||
}
|
||||
}
|
||||
|
||||
def TriggerZoneMapUpdate(): Unit = {
|
||||
if(Actor != ActorRef.noSender) Actor ! Building.TriggerZoneMapUpdate(Zone.Number)
|
||||
}
|
||||
|
||||
// Get all lattice neighbours matching the specified faction
|
||||
def Neighbours(faction: PlanetSideEmpire.Value): Option[Set[Building]] = {
|
||||
this.Neighbours match {
|
||||
|
|
@ -86,13 +102,7 @@ class Building(private val name: String,
|
|||
Int, Option[Additional3],
|
||||
Boolean, Boolean
|
||||
) = {
|
||||
//if we have a silo, get the NTU level
|
||||
val ntuLevel : Int = Amenities.find(_.Definition == GlobalDefinitions.resource_silo) match {
|
||||
case Some(obj: ResourceSilo) =>
|
||||
obj.CapacitorDisplay.toInt
|
||||
case _ => //we have no silo; we have unlimited power
|
||||
10
|
||||
}
|
||||
val ntuLevel : Int = NtuLevel
|
||||
//if we have a capture terminal, get the hack status & time (in milliseconds) from control console if it exists
|
||||
val (hacking, hackingFaction, hackTime) : (Boolean, PlanetSideEmpire.Value, Long) = Amenities.find(_.Definition == GlobalDefinitions.capture_terminal) match {
|
||||
case Some(obj: CaptureTerminal with Hackable) =>
|
||||
|
|
@ -118,6 +128,46 @@ class Building(private val name: String,
|
|||
(true, false)
|
||||
}
|
||||
}
|
||||
|
||||
val latticeBenefit : Int = {
|
||||
if(Faction == PlanetSideEmpire.NEUTRAL) 0
|
||||
else {
|
||||
def FindLatticeBenefit(wantedBenefit: ObjectDefinition, subGraph: Graph[Building, GraphEdge.UnDiEdge]): Boolean = {
|
||||
var found = false
|
||||
|
||||
subGraph find this match {
|
||||
case Some(self) =>
|
||||
if (this.Definition == wantedBenefit) found = true
|
||||
else {
|
||||
self pathUntil (_.Definition == wantedBenefit) match {
|
||||
case Some(_) => found = true
|
||||
case None => ;
|
||||
}
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
|
||||
found
|
||||
}
|
||||
|
||||
// Check this Building is on the lattice first
|
||||
zone.Lattice find this match {
|
||||
case Some(_) =>
|
||||
// todo: generator destruction state
|
||||
val subGraph = Zone.Lattice filter ((b: Building) => b.Faction == this.Faction && !b.CaptureConsoleIsHacked && b.NtuLevel > 0)
|
||||
|
||||
var stackedBenefit = 0
|
||||
if (FindLatticeBenefit(GlobalDefinitions.amp_station, subGraph)) stackedBenefit |= 1
|
||||
if (FindLatticeBenefit(GlobalDefinitions.comm_station_dsp, subGraph)) stackedBenefit |= 2
|
||||
if (FindLatticeBenefit(GlobalDefinitions.cryo_facility, subGraph)) stackedBenefit |= 4
|
||||
if (FindLatticeBenefit(GlobalDefinitions.comm_station, subGraph)) stackedBenefit |= 8
|
||||
if (FindLatticeBenefit(GlobalDefinitions.tech_plant, subGraph)) stackedBenefit |= 16
|
||||
|
||||
stackedBenefit
|
||||
case None => 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
//out
|
||||
(
|
||||
ntuLevel,
|
||||
|
|
@ -130,7 +180,7 @@ class Building(private val name: String,
|
|||
generatorState,
|
||||
spawnTubesNormal,
|
||||
false, //force_dome_active
|
||||
0, //lattice_benefit
|
||||
latticeBenefit,
|
||||
0, //cavern_benefit; !! Field > 0 will cause malformed packet. See class def.
|
||||
Nil,
|
||||
0,
|
||||
|
|
@ -196,4 +246,5 @@ object Building {
|
|||
}
|
||||
|
||||
final case class SendMapUpdate(all_clients: Boolean)
|
||||
final case class TriggerZoneMapUpdate(zone_num: Int)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.structures
|
|||
|
||||
import akka.actor.{Actor, ActorRef}
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.zones.InterstellarCluster
|
||||
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
||||
import services.ServiceManager
|
||||
import services.ServiceManager.Lookup
|
||||
|
|
@ -11,24 +12,31 @@ import services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, Gala
|
|||
class BuildingControl(building : Building) extends Actor with FactionAffinityBehavior.Check {
|
||||
def FactionObject : FactionAffinity = building
|
||||
var galaxyService : ActorRef = Actor.noSender
|
||||
var interstellarCluster : ActorRef = Actor.noSender
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
||||
override def preStart = {
|
||||
log.trace(s"Starting BuildingControl for ${building.GUID} / ${building.MapId}")
|
||||
ServiceManager.serviceManager ! Lookup("galaxy")
|
||||
ServiceManager.serviceManager ! Lookup("cluster")
|
||||
}
|
||||
|
||||
def receive : Receive = checkBehavior.orElse {
|
||||
case ServiceManager.LookupResult("galaxy", endpoint) =>
|
||||
galaxyService = endpoint
|
||||
log.trace("BuildingControl: Building " + building.GUID + " Got galaxy service " + endpoint)
|
||||
case ServiceManager.LookupResult("cluster", endpoint) =>
|
||||
interstellarCluster = endpoint
|
||||
log.trace("BuildingControl: Building " + building.GUID + " Got interstellar cluster service " + endpoint)
|
||||
|
||||
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||
val originalAffinity = building.Faction
|
||||
if(originalAffinity != (building.Faction = faction)) {
|
||||
building.Amenities.foreach(_.Actor forward FactionAffinity.ConfirmFactionAffinity())
|
||||
}
|
||||
sender ! FactionAffinity.AssertFactionAffinity(building, faction)
|
||||
|
||||
case Building.TriggerZoneMapUpdate(zone_num: Int) =>
|
||||
if(interstellarCluster != ActorRef.noSender) interstellarCluster ! InterstellarCluster.ZoneMapUpdate(zone_num)
|
||||
case Building.SendMapUpdate(all_clients: Boolean) =>
|
||||
val zoneNumber = building.Zone.Number
|
||||
val buildingNumber = building.MapId
|
||||
|
|
@ -57,7 +65,7 @@ class BuildingControl(building : Building) extends Actor with FactionAffinityBeh
|
|||
)
|
||||
|
||||
if(all_clients) {
|
||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(msg))
|
||||
if(galaxyService != ActorRef.noSender) galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(msg))
|
||||
} else {
|
||||
// Fake a GalaxyServiceResponse response back to just the sender
|
||||
sender ! GalaxyServiceResponse("", GalaxyResponse.MapUpdate(msg))
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
|
@ -69,6 +70,9 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
|
|||
case None => //zone_number does not exist
|
||||
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
|
||||
}
|
||||
case InterstellarCluster.ZoneMapUpdate(zone_num: Int) =>
|
||||
val zone = zones.find(x => x.Number == zone_num).get
|
||||
zone.Buildings.values.foreach(b => b.Actor ! Building.SendMapUpdate(all_clients = true))
|
||||
|
||||
case _ =>
|
||||
log.warn(s"InterstellarCluster received unknown message");
|
||||
|
|
@ -122,6 +126,13 @@ object InterstellarCluster {
|
|||
* @see `WorldSessionActor`
|
||||
*/
|
||||
final case class ClientInitializationComplete()
|
||||
|
||||
/**
|
||||
* Requests that all buildings within a zone send a map update for the purposes of refreshing lattice benefits, such as when a base is hacked, changes faction or loses power
|
||||
* @see `BuildingInfoUpdateMessage`
|
||||
* @param zone_num the zone number to request building map updates for
|
||||
*/
|
||||
final case class ZoneMapUpdate(zone_num: Int)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package services.local
|
|||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||
import net.psforever.objects.serverobject.terminals.{CaptureTerminal, Terminal}
|
||||
import net.psforever.objects.zones.Zone
|
||||
|
|
@ -87,10 +86,12 @@ class LocalService(zone : Zone) extends Actor {
|
|||
hackClearer ! HackClearActor.ObjectIsResecured(target)
|
||||
case LocalAction.HackCaptureTerminal(player_guid, _, target, unk1, unk2, isResecured) =>
|
||||
// When a CC is hacked (or resecured) all amenities for the base should be unhacked
|
||||
val hackableAmenities = target.Owner.asInstanceOf[Building].Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable])
|
||||
val building = target.Owner.asInstanceOf[Building]
|
||||
val hackableAmenities = building.Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable])
|
||||
hackableAmenities.foreach(amenity =>
|
||||
if(amenity.HackedBy.isDefined) { hackClearer ! HackClearActor.ObjectIsResecured(amenity) }
|
||||
)
|
||||
|
||||
if(isResecured){
|
||||
hackCapturer ! HackCaptureActor.ClearHack(target, zone)
|
||||
} else {
|
||||
|
|
@ -103,9 +104,16 @@ class LocalService(zone : Zone) extends Actor {
|
|||
hackCapturer ! HackCaptureActor.ObjectIsHacked(target, zone, unk1, unk2, duration = 1 nanosecond)
|
||||
}
|
||||
}
|
||||
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackCaptureTerminal(target.GUID, unk1, unk2, isResecured))
|
||||
)
|
||||
|
||||
// If the owner of this capture terminal is on the lattice trigger a zone wide map update to update lattice benefits
|
||||
zone.Lattice find building match {
|
||||
case Some(_) => building.TriggerZoneMapUpdate()
|
||||
case None => ;
|
||||
}
|
||||
case LocalAction.RouterTelepadTransport(player_guid, passenger_guid, src_guid, dest_guid) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.RouterTelepadTransport(passenger_guid, src_guid, dest_guid))
|
||||
|
|
@ -163,16 +171,8 @@ class LocalService(zone : Zone) extends Actor {
|
|||
case HackCaptureActor.HackTimeoutReached(capture_terminal_guid, _, _, _, hackedByFaction) =>
|
||||
val terminal = zone.GUID(capture_terminal_guid).get.asInstanceOf[CaptureTerminal]
|
||||
val building = terminal.Owner.asInstanceOf[Building]
|
||||
// todo: Move this to a function for Building
|
||||
var ntuLevel = building.Amenities.find(_.Definition == GlobalDefinitions.resource_silo) match {
|
||||
case Some(obj: ResourceSilo) =>
|
||||
obj.CapacitorDisplay.toInt
|
||||
case _ =>
|
||||
// Base has no NTU silo - likely a tower / cavern CC
|
||||
1
|
||||
}
|
||||
|
||||
if(ntuLevel > 0) {
|
||||
if(building.NtuLevel > 0) {
|
||||
log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction")
|
||||
|
||||
building.Faction = hackedByFaction
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class HackCaptureActor extends Actor {
|
|||
// Restart the timer, in case this is the first object in the hacked objects list or the object was removed and re-added
|
||||
RestartTimer()
|
||||
|
||||
target.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
if(target.isInstanceOf[CaptureTerminal]) { target.Owner.asInstanceOf[Building].TriggerZoneMapUpdate() }
|
||||
|
||||
case HackCaptureActor.ProcessCompleteHacks() =>
|
||||
log.trace("Processing complete hacks")
|
||||
|
|
@ -67,7 +67,7 @@ class HackCaptureActor extends Actor {
|
|||
case HackCaptureActor.ClearHack(target, _) =>
|
||||
hackedObjects = hackedObjects.filterNot(x => x.target == target)
|
||||
|
||||
if(target.isInstanceOf[CaptureTerminal]) { target.Owner.Actor ! Building.SendMapUpdate(all_clients = true) }
|
||||
if(target.isInstanceOf[CaptureTerminal]) { target.Owner.asInstanceOf[Building].TriggerZoneMapUpdate() }
|
||||
|
||||
// Restart the timer in case the object we just removed was the next one scheduled
|
||||
RestartTimer()
|
||||
|
|
|
|||
|
|
@ -696,7 +696,7 @@ class VehicleControlShieldsNotChargingDamagedTest extends ActorTest {
|
|||
val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel
|
||||
val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f), System.nanoTime)
|
||||
|
||||
"charge vehicle shields" in {
|
||||
"not charge vehicle shields if recently damaged" in {
|
||||
assert(vehicle.Shields == 0)
|
||||
vehicle.Actor ! Vitality.Damage({case v : Vehicle => v.History(obj)})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue