density-alerts

This commit is contained in:
ScrawnyRonnie 2025-07-21 10:38:08 -04:00 committed by Fate-JH
parent be30413714
commit 32287149c7
7 changed files with 89 additions and 4 deletions

View file

@ -508,6 +508,18 @@ game {
capture-experience-points-modifier = 1f
# Don't forget to pay back that debt.
}
alert {
# When a certain number of enemy players are within the SOI of a facility, an alert (DensityLevelUpdateMessage)
# will be dispatched to all players. Players of the owning faction will receive a chat warning (if in
# the same zone) and the map will flash the alert level over the facility until it changes
# Wiki says 25-30
yellow = 1
# Wiki says 30-60
orange = 2
# Wiki says 60+
red = 3
}
}
anti-cheat {

View file

@ -985,7 +985,7 @@ class ZoningOperations(
*/
def initFacility(continentNumber: Int, buildingNumber: Int, building: Building): Unit = {
sendResponse(building.infoUpdateMessage())
sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0, 0, 0, 0, 0, 0, 0, 0)))
sendResponse(building.densityLevelUpdateMessage(building))
}
/**

View file

@ -74,6 +74,8 @@ object BuildingActor {
final case class PowerOff() extends Command
final case class DensityLevelUpdate(building: Building) extends Command
/**
* Set a facility affiliated to one faction to be affiliated to a different faction.
* @param details building and event system references
@ -226,6 +228,7 @@ class BuildingActor(
case MapUpdate() =>
details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage()))
details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(details.building.densityLevelUpdateMessage(building)))
Behaviors.same
case AmenityStateChange(amenity, data) =>
@ -245,6 +248,10 @@ class BuildingActor(
case Ntu(msg) =>
logic.ntu(details, msg)
case DensityLevelUpdate(building) =>
details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(details.building.densityLevelUpdateMessage(building)))
Behaviors.same
}
}
}

View file

@ -11,13 +11,14 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.zones.Zone
import net.psforever.objects.zones.blockmap.BlockMapEntity
import net.psforever.packet.game.BuildingInfoUpdateMessage
import net.psforever.packet.game.{BuildingInfoUpdateMessage, DensityLevelUpdateMessage}
import net.psforever.types._
import scalax.collection.{Graph, GraphEdge}
import akka.actor.typed.scaladsl.adapter._
import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket}
import net.psforever.objects.serverobject.structures.participation.{MajorFacilityHackParticipation, NoParticipation, ParticipationLogic, TowerHackParticipation}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
import net.psforever.util.Config
class Building(
private val name: String,
@ -236,6 +237,46 @@ class Building(
)
}
def densityLevelUpdateMessage(building: Building): DensityLevelUpdateMessage = {
if (building.PlayersInSOI.nonEmpty) {
val factionCounts: Map[PlanetSideEmpire.Value, Int] =
building.PlayersInSOI.groupBy(_.Faction).view.mapValues(_.size).toMap
val otherEmpireCounts: Map[PlanetSideEmpire.Value, Int] = PlanetSideEmpire.values.map {
faction =>
val otherCount = factionCounts.filterNot(_._1 == faction).values.sum
faction -> otherCount
}.toMap
val trAlert = otherEmpireCounts.getOrElse(PlanetSideEmpire.TR, 0) match {
case count if count >= Config.app.game.alert.red => 3
case count if count >= Config.app.game.alert.orange => 2
case count if count >= Config.app.game.alert.yellow => 1
case _ => 0
}
val ncAlert = otherEmpireCounts.getOrElse(PlanetSideEmpire.NC, 0) match {
case count if count >= Config.app.game.alert.red => 3
case count if count >= Config.app.game.alert.orange => 2
case count if count >= Config.app.game.alert.yellow => 1
case _ => 0
}
val vsAlert = otherEmpireCounts.getOrElse(PlanetSideEmpire.VS, 0) match {
case count if count >= Config.app.game.alert.red => 3
case count if count >= Config.app.game.alert.orange => 2
case count if count >= Config.app.game.alert.yellow => 1
case _ => 0
}
val boAlert = otherEmpireCounts.getOrElse(PlanetSideEmpire.NEUTRAL, 0) match {
case count if count >= Config.app.game.alert.red => 3
case count if count >= Config.app.game.alert.orange => 2
case count if count >= Config.app.game.alert.yellow => 1
case _ => 0
}
DensityLevelUpdateMessage(Zone.Number, MapId, List(0, trAlert, 0, ncAlert, 0, vsAlert, 0, boAlert))
}
else { //nobody is in this SOI
DensityLevelUpdateMessage(Zone.Number, MapId, List(0, 0, 0, 0, 0, 0, 0, 0))
}
}
def hasLatticeBenefit(wantedBenefit: LatticeBenefit): Boolean = {
val baseDownState = (NtuSource match {
case Some(ntu) => ntu.NtuCapacitor < 1f

View file

@ -9,6 +9,7 @@ import net.psforever.types.{ChatMessageType, PlanetSideEmpire, Vector3}
import net.psforever.util.Config
import akka.pattern.ask
import akka.util.Timeout
import net.psforever.actors.zone.BuildingActor
import net.psforever.objects.Player
import net.psforever.objects.avatar.scoring.Kill
import net.psforever.objects.serverobject.hackable.Hackable
@ -26,6 +27,8 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci
private var hotSpotLayersOverTime: Seq[List[HotSpotInfo]] = Seq[List[HotSpotInfo]]()
var lastEnemyCount: List[Player] = List.empty
def TryUpdate(): Unit = {
val list = building.PlayersInSOI
if (list.nonEmpty) {
@ -37,6 +40,14 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci
updateHotSpotInfoOverTime()
updateTime(now)
}
val enemies = list.filter(p => p.Faction != building.Faction) ++
building.Zone.blockMap.sector(building).corpseList
.filter(p => Vector3.DistanceSquared(building.Position.xy, p.Position.xy) < building.Definition.SOIRadius * building.Definition.SOIRadius)
//alert defenders (actually goes to all clients) of population change for base alerts
if (Math.abs(enemies.length - lastEnemyCount.length) >= 1) {
building.Actor ! BuildingActor.DensityLevelUpdate(building)
}
lastEnemyCount = enemies
building.CaptureTerminal
.map(_.HackedBy)
.collect {

View file

@ -55,9 +55,16 @@ class SphereOfInfluenceActor(zone: Zone) extends Actor {
sois.foreach {
case (facility, radius) =>
val facilityXY = facility.Position.xy
facility.PlayersInSOI = zone.blockMap.sector(facility)
val playersOnFoot = zone.blockMap.sector(facility)
.livePlayerList
.filter(p => Vector3.DistanceSquared(facilityXY, p.Position.xy) < radius)
val vehicleOccupants = zone.blockMap.sector(facility)
.vehicleList
.filter(v => Vector3.DistanceSquared(facilityXY, v.Position.xy) < radius)
.flatMap(_.Seats.values.flatMap(_.occupants))
facility.PlayersInSOI = playersOnFoot ++ vehicleOccupants
}
populateTick.cancel()
populateTick = context.system.scheduler.scheduleOnce(5 seconds, self, SOI.Populate())

View file

@ -164,7 +164,8 @@ case class GameConfig(
experience: Experience,
maxBattleRank: Int,
promotion: PromotionSystem,
facilityHackTime: FiniteDuration
facilityHackTime: FiniteDuration,
alert: DensityAlert
)
case class InstantActionConfig(
@ -326,3 +327,9 @@ case class PromotionSystem(
supportExperiencePointsModifier: Float,
captureExperiencePointsModifier: Float
)
case class DensityAlert(
yellow: Int,
orange: Int,
red: Int
)