mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
Instant Action / Recall to Sanctuary (#348)
* refactored ZoneActor for external calls; earliest code for calculating Instant Action placement * created a building definition so that SOI is no longer indeterminate; gave hot spots projector a longer-lasting backup for purposes of activity retention; instant action ramp-up works * filled out instant action messages; refactored main method * packet and initial tests for DroppodFreefallingMessage; drop pod definition, packet converter, and consideration in WSA and InterstellarCluster instant action functionality; droppods now work * duplicated soi information; modified priority of instant action; assigned cavern status; added reset for instant action failure; implant interrupt condition; wrote comments * no instant action droppods; added messages for cancelling instant action when certain conditions occur; wilderness instant action request * made generic the entire instant action process to shoehorn the whole of the sanctuary recall process into it; I hope you're happy * test fix; vehicle hacking fix; no more artificial NTU drain * escape case for zoning last chance; descriptive mesages condense similar calls * something of a merge repair
This commit is contained in:
parent
c80bb2836f
commit
a23643b240
|
|
@ -17,7 +17,7 @@ import net.psforever.objects.serverobject.painbox.PainboxDefinition
|
|||
import net.psforever.objects.serverobject.terminals._
|
||||
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
|
||||
import net.psforever.objects.serverobject.structures.SphereOfInfluence
|
||||
import net.psforever.objects.serverobject.structures.{BuildingDefinition, WarpGateDefinition}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade}
|
||||
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, SeatArmorRestriction, UtilityType}
|
||||
import net.psforever.objects.vital.{DamageType, StandardMaxDamage, StandardResolutions}
|
||||
|
|
@ -897,6 +897,8 @@ object GlobalDefinitions {
|
|||
val lodestar = VehicleDefinition(ObjectClass.lodestar)
|
||||
|
||||
val phantasm = VehicleDefinition(ObjectClass.phantasm)
|
||||
|
||||
val droppod = VehicleDefinition(ObjectClass.droppod)
|
||||
init_vehicles()
|
||||
|
||||
/*
|
||||
|
|
@ -1055,41 +1057,42 @@ object GlobalDefinitions {
|
|||
/*
|
||||
Buildings
|
||||
*/
|
||||
val building : ObjectDefinition = new ObjectDefinition(474) { Name = "building" } //borrows object id of entity mainbase1
|
||||
val amp_station : ObjectDefinition = new ObjectDefinition(45) with SphereOfInfluence { Name = "amp_station"; SOIRadius = 300 }
|
||||
val comm_station : ObjectDefinition = new ObjectDefinition(211) with SphereOfInfluence { Name = "comm_station"; SOIRadius = 300 }
|
||||
val comm_station_dsp : ObjectDefinition = new ObjectDefinition(212) with SphereOfInfluence { Name = "comm_station_dsp"; SOIRadius = 300 }
|
||||
val cryo_facility : ObjectDefinition = new ObjectDefinition(215) with SphereOfInfluence { Name = "cryo_facility"; SOIRadius = 300 }
|
||||
val building = new BuildingDefinition(474) { Name = "building" } //borrows object id of entity mainbase1
|
||||
val amp_station = new BuildingDefinition(45) { Name = "amp_station"; SOIRadius = 300 }
|
||||
val comm_station = new BuildingDefinition(211) { Name = "comm_station"; SOIRadius = 300 }
|
||||
val comm_station_dsp = new BuildingDefinition(212) { Name = "comm_station_dsp"; SOIRadius = 300 }
|
||||
val cryo_facility = new BuildingDefinition(215) { Name = "cryo_facility"; SOIRadius = 300 }
|
||||
|
||||
val vanu_core : ObjectDefinition = new ObjectDefinition(932) { Name = "vanu_core" }
|
||||
val vanu_core = new BuildingDefinition(932) { Name = "vanu_core" }
|
||||
|
||||
val ground_bldg_a : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_a" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_b : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_b" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_c : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_c" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_d : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_d" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_e : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_e" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_f : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_f" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_g : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_g" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_h : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_h" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_i : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_i" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_j : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_j" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_z : ObjectDefinition = new ObjectDefinition(474) { Name = "ground_bldg_z" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_a = new BuildingDefinition(474) { Name = "ground_bldg_a" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_b = new BuildingDefinition(474) { Name = "ground_bldg_b" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_c = new BuildingDefinition(474) { Name = "ground_bldg_c" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_d = new BuildingDefinition(474) { Name = "ground_bldg_d" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_e = new BuildingDefinition(474) { Name = "ground_bldg_e" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_f = new BuildingDefinition(474) { Name = "ground_bldg_f" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_g = new BuildingDefinition(474) { Name = "ground_bldg_g" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_h = new BuildingDefinition(474) { Name = "ground_bldg_h" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_i = new BuildingDefinition(474) { Name = "ground_bldg_i" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_j = new BuildingDefinition(474) { Name = "ground_bldg_j" } //borrows object id of entity mainbase1
|
||||
val ground_bldg_z = new BuildingDefinition(474) { Name = "ground_bldg_z" } //borrows object id of entity mainbase1
|
||||
|
||||
val ceiling_bldg_a : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_a" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_b : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_b" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_c : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_c" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_d : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_d" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_e : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_e" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_f : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_f" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_g : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_g" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_h : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_h" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_i : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_i" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_j : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_j" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_z : ObjectDefinition = new ObjectDefinition(474) { Name = "ceiling_bldg_z" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_a = new BuildingDefinition(474) { Name = "ceiling_bldg_a" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_b = new BuildingDefinition(474) { Name = "ceiling_bldg_b" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_c = new BuildingDefinition(474) { Name = "ceiling_bldg_c" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_d = new BuildingDefinition(474) { Name = "ceiling_bldg_d" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_e = new BuildingDefinition(474) { Name = "ceiling_bldg_e" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_f = new BuildingDefinition(474) { Name = "ceiling_bldg_f" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_g = new BuildingDefinition(474) { Name = "ceiling_bldg_g" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_h = new BuildingDefinition(474) { Name = "ceiling_bldg_h" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_i = new BuildingDefinition(474) { Name = "ceiling_bldg_i" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_j = new BuildingDefinition(474) { Name = "ceiling_bldg_j" } //borrows object id of entity mainbase1
|
||||
val ceiling_bldg_z = new BuildingDefinition(474) { Name = "ceiling_bldg_z" } //borrows object id of entity mainbase1
|
||||
|
||||
val hst : ObjectDefinition with SpawnPointDefinition = new ObjectDefinition(402) with SpawnPointDefinition
|
||||
val hst = new WarpGateDefinition(402)
|
||||
hst.Name = "hst"
|
||||
hst.UseRadius = 20.4810f
|
||||
hst.SOIRadius = 21
|
||||
hst.VehicleAllowance = true
|
||||
hst.NoWarp += dropship
|
||||
hst.NoWarp += galaxy_gunship
|
||||
|
|
@ -1102,54 +1105,57 @@ object GlobalDefinitions {
|
|||
//hst.NoWarp += peregrine_flight
|
||||
hst.SpecificPointFunc = SpawnPoint.Gate
|
||||
|
||||
val mainbase1 : ObjectDefinition = new ObjectDefinition(474) { Name = "mainbase1" }
|
||||
val mainbase2 : ObjectDefinition = new ObjectDefinition(475) { Name = "mainbase2" }
|
||||
val mainbase3 : ObjectDefinition = new ObjectDefinition(476) { Name = "mainbase3" }
|
||||
val meeting_center_nc : ObjectDefinition = new ObjectDefinition(537) { Name = "meeting_center_nc" }
|
||||
val meeting_center_tr : ObjectDefinition = new ObjectDefinition(538) { Name = "meeting_center_tr" }
|
||||
val meeting_center_vs : ObjectDefinition = new ObjectDefinition(539) { Name = "meeting_center_vs" }
|
||||
val minibase1 : ObjectDefinition = new ObjectDefinition(557) { Name = "minibase1" }
|
||||
val minibase2 : ObjectDefinition = new ObjectDefinition(558) { Name = "minibase2" }
|
||||
val minibase3 : ObjectDefinition = new ObjectDefinition(559) { Name = "minibase3" }
|
||||
val redoubt : ObjectDefinition = new ObjectDefinition(726) with SphereOfInfluence { Name = "redoubt"; SOIRadius = 187 }
|
||||
val tech_plant : ObjectDefinition = new ObjectDefinition(852) with SphereOfInfluence { Name = "tech_plant"; SOIRadius = 300 }
|
||||
val tower_a : ObjectDefinition = new ObjectDefinition(869) with SphereOfInfluence { Name = "tower_a"; SOIRadius = 50 }
|
||||
val tower_b : ObjectDefinition = new ObjectDefinition(870) with SphereOfInfluence { Name = "tower_b"; SOIRadius = 50 }
|
||||
val tower_c : ObjectDefinition = new ObjectDefinition(871) with SphereOfInfluence { Name = "tower_c"; SOIRadius = 50 }
|
||||
val vanu_control_point : ObjectDefinition = new ObjectDefinition(931) with SphereOfInfluence { Name = "vanu_control_point"; SOIRadius = 187 }
|
||||
val vanu_vehicle_station : ObjectDefinition = new ObjectDefinition(948) with SphereOfInfluence { Name = "vanu_vehicle_station"; SOIRadius = 187 }
|
||||
val mainbase1 = new BuildingDefinition(474) { Name = "mainbase1" }
|
||||
val mainbase2 = new BuildingDefinition(475) { Name = "mainbase2" }
|
||||
val mainbase3 = new BuildingDefinition(476) { Name = "mainbase3" }
|
||||
val meeting_center_nc = new BuildingDefinition(537) { Name = "meeting_center_nc" }
|
||||
val meeting_center_tr = new BuildingDefinition(538) { Name = "meeting_center_tr" }
|
||||
val meeting_center_vs = new BuildingDefinition(539) { Name = "meeting_center_vs" }
|
||||
val minibase1 = new BuildingDefinition(557) { Name = "minibase1" }
|
||||
val minibase2 = new BuildingDefinition(558) { Name = "minibase2" }
|
||||
val minibase3 = new BuildingDefinition(559) { Name = "minibase3" }
|
||||
val redoubt = new BuildingDefinition(726) { Name = "redoubt"; SOIRadius = 187 }
|
||||
val tech_plant = new BuildingDefinition(852) { Name = "tech_plant"; SOIRadius = 300 }
|
||||
val tower_a = new BuildingDefinition(869) { Name = "tower_a"; SOIRadius = 50 }
|
||||
val tower_b = new BuildingDefinition(870) { Name = "tower_b"; SOIRadius = 50 }
|
||||
val tower_c = new BuildingDefinition(871) { Name = "tower_c"; SOIRadius = 50 }
|
||||
val vanu_control_point = new BuildingDefinition(931) { Name = "vanu_control_point"; SOIRadius = 187 }
|
||||
val vanu_vehicle_station = new BuildingDefinition(948) { Name = "vanu_vehicle_station"; SOIRadius = 187 }
|
||||
|
||||
val warpgate : ObjectDefinition with SpawnPointDefinition = new ObjectDefinition(993) with SpawnPointDefinition
|
||||
val warpgate = new WarpGateDefinition(993)
|
||||
warpgate.Name = "warpgate"
|
||||
warpgate.UseRadius = 301.8713f
|
||||
warpgate.SOIRadius = 302
|
||||
warpgate.VehicleAllowance = true
|
||||
warpgate.SpecificPointFunc = SpawnPoint.Gate
|
||||
|
||||
val warpgate_cavern : ObjectDefinition with SpawnPointDefinition = new ObjectDefinition(994) with SpawnPointDefinition
|
||||
val warpgate_cavern = new WarpGateDefinition(994)
|
||||
warpgate_cavern.Name = "warpgate_cavern"
|
||||
warpgate_cavern.UseRadius = 51.0522f
|
||||
warpgate_cavern.SOIRadius = 52
|
||||
warpgate_cavern.VehicleAllowance = true
|
||||
warpgate_cavern.SpecificPointFunc = SpawnPoint.Gate
|
||||
|
||||
val warpgate_small : ObjectDefinition with SpawnPointDefinition = new ObjectDefinition(995) with SpawnPointDefinition
|
||||
val warpgate_small = new WarpGateDefinition(995)
|
||||
warpgate_small.Name = "warpgate_small"
|
||||
warpgate_small.UseRadius = 103f
|
||||
warpgate_small.SOIRadius = 103
|
||||
warpgate_small.VehicleAllowance = true
|
||||
warpgate_small.SpecificPointFunc = SpawnPoint.Gate
|
||||
|
||||
val bunker_gauntlet : ObjectDefinition = new ObjectDefinition(150) { Name = "bunker_gauntlet" }
|
||||
val bunker_lg : ObjectDefinition = new ObjectDefinition(151) { Name = "bunker_lg" }
|
||||
val bunker_sm : ObjectDefinition = new ObjectDefinition(152) { Name = "bunker_sm" }
|
||||
val bunker_gauntlet = new BuildingDefinition(150) { Name = "bunker_gauntlet" }
|
||||
val bunker_lg = new BuildingDefinition(151) { Name = "bunker_lg" }
|
||||
val bunker_sm = new BuildingDefinition(152) { Name = "bunker_sm" }
|
||||
|
||||
val orbital_building_nc : ObjectDefinition = new ObjectDefinition(605) { Name = "orbital_building_nc" }
|
||||
val orbital_building_tr : ObjectDefinition = new ObjectDefinition(606) { Name = "orbital_building_tr" }
|
||||
val orbital_building_vs : ObjectDefinition = new ObjectDefinition(607) { Name = "orbital_building_vs" }
|
||||
val VT_building_nc : ObjectDefinition = new ObjectDefinition(978) { Name = "VT_building_nc" }
|
||||
val VT_building_tr : ObjectDefinition = new ObjectDefinition(979) { Name = "VT_building_tr" }
|
||||
val VT_building_vs : ObjectDefinition = new ObjectDefinition(980) { Name = "VT_building_vs" }
|
||||
val vt_dropship : ObjectDefinition = new ObjectDefinition(981) { Name = "vt_dropship" }
|
||||
val vt_spawn : ObjectDefinition = new ObjectDefinition(984) { Name = "vt_spawn" }
|
||||
val vt_vehicle : ObjectDefinition = new ObjectDefinition(985) { Name = "vt_vehicle" }
|
||||
val orbital_building_nc = new BuildingDefinition(605) { Name = "orbital_building_nc" }
|
||||
val orbital_building_tr = new BuildingDefinition(606) { Name = "orbital_building_tr" }
|
||||
val orbital_building_vs = new BuildingDefinition(607) { Name = "orbital_building_vs" }
|
||||
val VT_building_nc = new BuildingDefinition(978) { Name = "VT_building_nc" }
|
||||
val VT_building_tr = new BuildingDefinition(979) { Name = "VT_building_tr" }
|
||||
val VT_building_vs = new BuildingDefinition(980) { Name = "VT_building_vs" }
|
||||
val vt_dropship = new BuildingDefinition(981) { Name = "vt_dropship" }
|
||||
val vt_spawn = new BuildingDefinition(984) { Name = "vt_spawn" }
|
||||
val vt_vehicle = new BuildingDefinition(985) { Name = "vt_vehicle" }
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -6083,6 +6089,17 @@ object GlobalDefinitions {
|
|||
phantasm.Packet = variantConverter
|
||||
phantasm.DestroyedModel = None //the adb calls out a phantasm_destroyed but no such asset exists
|
||||
phantasm.JackingDuration = Array(0, 60, 20, 10)
|
||||
|
||||
droppod.Name = "droppod"
|
||||
droppod.MaxHealth = 20000
|
||||
//droppod.Damageable = false
|
||||
droppod.CanFly = true
|
||||
droppod.Seats += 0 -> new SeatDefinition
|
||||
droppod.MountPoints += 1 -> 0
|
||||
droppod.TrunkSize = InventoryTile.None
|
||||
droppod.Packet = new DroppodConverter()
|
||||
droppod.DeconstructionTime = Some(5 seconds)
|
||||
droppod.DestroyedModel = None //the adb calls out a droppod; the cyclic nature of this confound me
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects.vehicles.{CargoBehavior, VehicleLockState}
|
|||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.TriggeredSound
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
||||
import services.RemoverActor
|
||||
import services.{RemoverActor, Service}
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
|
@ -244,8 +244,7 @@ object Vehicles {
|
|||
Vehicles.Own(target, hacker)
|
||||
//todo: Send HackMessage -> HackCleared to vehicle? can be found in packet captures. Not sure if necessary.
|
||||
// And broadcast the faction change to other clients
|
||||
zone.AvatarEvents ! AvatarServiceMessage(hacker.Name, AvatarAction.SetEmpire(hacker.GUID, target.GUID, hacker.Faction))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.SetEmpire(hacker.GUID, target.GUID, hacker.Faction))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction))
|
||||
}
|
||||
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.TriggerSound(hacker.GUID, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f))
|
||||
// Clean up after specific vehicles, e.g. remove router telepads
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class DroppodConverter extends ObjectCreateConverter[Vehicle]() {
|
||||
override def DetailedConstructorData(obj : Vehicle) : Try[DroppodData] =
|
||||
Failure(new Exception("DroppodConverter should not be used to generate detailed DroppodData (nothing should)"))
|
||||
|
||||
override def ConstructorData(obj : Vehicle) : Try[DroppodData] = {
|
||||
val health = StatConverter.Health(obj.Health, obj.MaxHealth)
|
||||
if(health > 0) { //active
|
||||
Success(
|
||||
DroppodData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
jammered = obj.Jammed,
|
||||
v4 = Some(false),
|
||||
v5 = None,
|
||||
obj.Owner match {
|
||||
case Some(owner) => owner
|
||||
case None => PlanetSideGUID(0)
|
||||
}
|
||||
)
|
||||
),
|
||||
health,
|
||||
burn = false,
|
||||
unk = false
|
||||
)
|
||||
)
|
||||
}
|
||||
else { //destroyed
|
||||
Success(
|
||||
DroppodData(
|
||||
CommonFieldDataWithPlacement(
|
||||
PlacementData(obj.Position, obj.Orientation, obj.Velocity),
|
||||
CommonFieldData(
|
||||
obj.Faction,
|
||||
bops = false,
|
||||
alternate = false,
|
||||
v1 = false,
|
||||
v2 = None,
|
||||
jammered = false,
|
||||
v4 = Some(false),
|
||||
v5 = None,
|
||||
PlanetSideGUID(0)
|
||||
)
|
||||
),
|
||||
0,
|
||||
burn = false,
|
||||
unk = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with Factio
|
|||
def receive : Receive = {
|
||||
case "startup" =>
|
||||
// todo: This is just a temporary solution to drain NTU over time. When base object destruction is properly implemented NTU should be deducted when base objects repair themselves
|
||||
context.system.scheduler.schedule(5 second, 5 second, self, ResourceSilo.UpdateChargeLevel(-1))
|
||||
// context.system.scheduler.schedule(5 second, 5 second, self, ResourceSilo.UpdateChargeLevel(-1))
|
||||
context.become(Processing)
|
||||
|
||||
case _ => ;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class Building(private val name: String,
|
|||
private val map_id : Int,
|
||||
private val zone : Zone,
|
||||
private val buildingType : StructureType.Value,
|
||||
private val buildingDefinition : ObjectDefinition) extends AmenityOwner {
|
||||
private val buildingDefinition : BuildingDefinition) extends AmenityOwner {
|
||||
/**
|
||||
* The map_id is the identifier number used in BuildingInfoUpdateMessage. This is the index that the building appears in the MPO file starting from index 1
|
||||
* The GUID is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage.
|
||||
|
|
@ -267,7 +267,7 @@ class Building(private val name: String,
|
|||
|
||||
override def Continent_=(zone : String) : String = Continent //building never leaves zone after being set in constructor
|
||||
|
||||
def Definition: ObjectDefinition = buildingDefinition
|
||||
def Definition: BuildingDefinition = buildingDefinition
|
||||
}
|
||||
|
||||
object Building {
|
||||
|
|
@ -281,7 +281,7 @@ object Building {
|
|||
new Building(name, guid, map_id, zone, buildingType, GlobalDefinitions.building)
|
||||
}
|
||||
|
||||
def Structure(buildingType : StructureType.Value, location : Vector3, definition: ObjectDefinition)(name : String, guid : Int, map_id : Int, zone : Zone, context : ActorContext) : Building = {
|
||||
def Structure(buildingType : StructureType.Value, location : Vector3, definition: BuildingDefinition)(name : String, guid : Int, map_id : Int, zone : Zone, context : ActorContext) : Building = {
|
||||
import akka.actor.Props
|
||||
val obj = new Building(name, guid, map_id, zone, buildingType, definition)
|
||||
obj.Position = location
|
||||
|
|
@ -305,7 +305,7 @@ object Building {
|
|||
obj
|
||||
}
|
||||
|
||||
def Structure(buildingType : StructureType.Value, buildingDefinition : ObjectDefinition, location : Vector3)(name: String, guid: Int, id : Int, zone : Zone, context : ActorContext) : Building = {
|
||||
def Structure(buildingType : StructureType.Value, buildingDefinition : BuildingDefinition, location : Vector3)(name: String, guid: Int, id : Int, zone : Zone, context : ActorContext) : Building = {
|
||||
import akka.actor.Props
|
||||
val obj = new Building(name, guid, id, zone, buildingType, buildingDefinition)
|
||||
obj.Position = location
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package net.psforever.objects.serverobject.structures
|
||||
|
||||
import net.psforever.objects.SpawnPointDefinition
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
|
||||
class BuildingDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||
with SphereOfInfluence {
|
||||
Name = "building"
|
||||
}
|
||||
|
||||
class WarpGateDefinition(objectId : Int) extends BuildingDefinition(objectId)
|
||||
with SpawnPointDefinition
|
||||
|
|
@ -2,16 +2,15 @@
|
|||
package net.psforever.objects.serverobject.structures
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.{GlobalDefinitions, SpawnPoint, SpawnPointDefinition}
|
||||
import net.psforever.objects.{GlobalDefinitions, SpawnPoint}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{Additional1, Additional2, Additional3}
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGeneratorState, Vector3}
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
class WarpGate(name : String, building_guid : Int, map_id : Int, zone : Zone, buildingDefinition : ObjectDefinition with SpawnPointDefinition)
|
||||
class WarpGate(name : String, building_guid : Int, map_id : Int, zone : Zone, buildingDefinition : WarpGateDefinition)
|
||||
extends Building(name, building_guid, map_id, zone, StructureType.WarpGate, buildingDefinition)
|
||||
with SpawnPoint {
|
||||
/** can this building be used as an active warp gate */
|
||||
|
|
@ -155,12 +154,12 @@ class WarpGate(name : String, building_guid : Int, map_id : Int, zone : Zone, bu
|
|||
|
||||
def Owner : PlanetSideServerObject = this
|
||||
|
||||
override def Definition : ObjectDefinition with SpawnPointDefinition = buildingDefinition
|
||||
override def Definition : WarpGateDefinition = buildingDefinition
|
||||
//TODO stuff later
|
||||
}
|
||||
|
||||
object WarpGate {
|
||||
def apply(name : String, guid : Int, map_id : Int, zone : Zone, buildingDefinition : ObjectDefinition with SpawnPointDefinition) : WarpGate = {
|
||||
def apply(name : String, guid : Int, map_id : Int, zone : Zone, buildingDefinition : WarpGateDefinition) : WarpGate = {
|
||||
new WarpGate(name, guid, map_id, zone, buildingDefinition)
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +178,7 @@ object WarpGate {
|
|||
obj
|
||||
}
|
||||
|
||||
def Structure(location : Vector3, buildingDefinition : ObjectDefinition with SpawnPointDefinition)(name : String, guid : Int, map_id : Int, zone : Zone, context : ActorContext) : WarpGate = {
|
||||
def Structure(location : Vector3, buildingDefinition : WarpGateDefinition)(name : String, guid : Int, map_id : Int, zone : Zone, context : ActorContext) : WarpGate = {
|
||||
import akka.actor.Props
|
||||
val obj = new WarpGate(name, guid, map_id, zone, buildingDefinition)
|
||||
obj.Position = location
|
||||
|
|
|
|||
|
|
@ -77,7 +77,12 @@ class ActivityReport {
|
|||
* As a `Long` value, if there was no previous report, the value will be considered `0L`.
|
||||
* @return the time of the last activity report
|
||||
*/
|
||||
def LastReport : Long = lastReport match { case Some(t) => t; case _ => 0L }
|
||||
def LastReport : Long = lastReport.getOrElse(0L)
|
||||
|
||||
def SetLastReport(time : Long) : Long = {
|
||||
lastReport = Some(time)
|
||||
LastReport
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of time that this (ongoing) activity is relevant.
|
||||
|
|
@ -130,6 +135,16 @@ class ActivityReport {
|
|||
this
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit new activity.
|
||||
* Do not increase the lifespan of the current report's existence.
|
||||
* @return the current report
|
||||
*/
|
||||
def ReportOld(pow : Int) : ActivityReport = {
|
||||
RaiseHeat(pow)
|
||||
this
|
||||
}
|
||||
|
||||
private def RaiseHeat(addHeat : Int) : Int = {
|
||||
if(addHeat < (Integer.MAX_VALUE - heat)) {
|
||||
heat += addHeat
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ package net.psforever.objects.zones
|
|||
|
||||
import akka.actor.{Actor, Props}
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.Random
|
||||
|
||||
/**
|
||||
* The root of the universe of one-continent planets, codified by the game's "Interstellar Map."
|
||||
|
|
@ -23,6 +25,7 @@ import scala.annotation.tailrec
|
|||
*/
|
||||
class InterstellarCluster(zones : List[Zone]) extends Actor {
|
||||
private[this] val log = org.log4s.getLogger
|
||||
val recallRandom = new Random()
|
||||
log.info("Starting interplanetary cluster ...")
|
||||
|
||||
/**
|
||||
|
|
@ -53,7 +56,7 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
|
|||
zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) })
|
||||
sender ! InterstellarCluster.ClientInitializationComplete() //will be processed after all Zones
|
||||
|
||||
case msg @ Zone.Lattice.RequestSpawnPoint(zone_number, _, _) =>
|
||||
case msg @ Zone.Lattice.RequestSpawnPoint(zone_number, _, _, _) =>
|
||||
recursiveFindWorldInCluster(zones.iterator, _.Number == zone_number) match {
|
||||
case Some(zone) =>
|
||||
zone.Actor forward msg
|
||||
|
|
@ -62,7 +65,7 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
|
|||
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
|
||||
}
|
||||
|
||||
case msg @ Zone.Lattice.RequestSpecificSpawnPoint(zone_number, _, _) =>
|
||||
case msg @ Zone.Lattice.RequestSpecificSpawnPoint(zone_number, _, _, _) =>
|
||||
recursiveFindWorldInCluster(zones.iterator, _.Number == zone_number) match {
|
||||
case Some(zone) =>
|
||||
zone.Actor forward msg
|
||||
|
|
@ -74,6 +77,74 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
|
|||
val zone = zones.find(x => x.Number == zone_num).get
|
||||
zone.Buildings.values.foreach(b => b.Actor ! Building.SendMapUpdate(all_clients = true))
|
||||
|
||||
|
||||
case Zoning.InstantAction.Request(faction) =>
|
||||
val interests = zones.flatMap { zone =>
|
||||
//TODO zone.Locked.contains(faction)
|
||||
zone.HotSpotData
|
||||
.collect { case spot if zone.Players.nonEmpty => (zone, spot) }
|
||||
} /* ignore zones without existing population */
|
||||
if(interests.nonEmpty) {
|
||||
val (withAllies, onlyEnemies) = interests
|
||||
.map { case (zone, spot) =>
|
||||
(
|
||||
zone,
|
||||
spot,
|
||||
ZoneActor.FindLocalSpawnPointsInZone(zone, spot.DisplayLocation, faction, 0).getOrElse(Nil)
|
||||
)
|
||||
} /* pair hotspots and spawn points */
|
||||
.filter { case (_, _, spawns) => spawns.nonEmpty } /* faction spawns must exist */
|
||||
.sortBy({ case (_, spot, _) => spot.Activity.values.foldLeft(0)(_ + _.Heat) })(Ordering[Int].reverse) /* greatest > least */
|
||||
.partition { case (_, spot, _) => spot.ActivityBy().contains(faction) } /* us versus them */
|
||||
withAllies.headOption.orElse(onlyEnemies.headOption) match {
|
||||
case Some((zone, info, List(spawnPoint))) =>
|
||||
//one spawn
|
||||
val pos = info.DisplayLocation
|
||||
sender ! Zoning.InstantAction.Located(zone, pos, spawnPoint)
|
||||
case Some((zone, info, spawns)) =>
|
||||
//multiple spawn options
|
||||
val pos = info.DisplayLocation
|
||||
val spawnPoint = spawns.minBy(point => Vector3.DistanceSquared(point.Position, pos))
|
||||
sender ! Zoning.InstantAction.Located(zone, pos, spawnPoint)
|
||||
case None =>
|
||||
//no actionable hot spots
|
||||
sender ! Zoning.InstantAction.NotLocated()
|
||||
}
|
||||
}
|
||||
else {
|
||||
//never had any actionable hot spots
|
||||
sender ! Zoning.InstantAction.NotLocated()
|
||||
}
|
||||
|
||||
case Zoning.Recall.Request(faction, sanctuary_id) =>
|
||||
recursiveFindWorldInCluster(zones.iterator, _.Id.equals(sanctuary_id)) match {
|
||||
case Some(zone) =>
|
||||
//TODO zone full
|
||||
val width = zone.Map.Scale.width
|
||||
val height = zone.Map.Scale.height
|
||||
//xy-coordinates indicate sanctuary spawn bias:
|
||||
val spot = math.abs(scala.util.Random.nextInt() % sender.toString.hashCode % 4) match {
|
||||
case 0 => Vector3(width, height, 0) //NE
|
||||
case 1 => Vector3(width, 0, 0) //SE
|
||||
case 2 => Vector3.Zero //SW
|
||||
case 3 => Vector3(0, height, 0) //NW
|
||||
}
|
||||
ZoneActor.FindLocalSpawnPointsInZone(zone, spot, faction, 7).getOrElse(Nil) match {
|
||||
case Nil =>
|
||||
//no spawns
|
||||
sender ! Zoning.Recall.Denied("unavailable")
|
||||
case List(spawnPoint) =>
|
||||
//one spawn
|
||||
sender ! Zoning.Recall.Located(zone, spawnPoint)
|
||||
case spawnPoints =>
|
||||
//multiple spawn options
|
||||
val spawnPoint = spawnPoints(recallRandom.nextInt(spawnPoints.length))
|
||||
sender ! Zoning.Recall.Located(zone, spawnPoint)
|
||||
}
|
||||
case None =>
|
||||
sender ! Zoning.Recall.Denied("unavailable")
|
||||
}
|
||||
|
||||
case _ =>
|
||||
log.warn(s"InterstellarCluster received unknown message");
|
||||
}
|
||||
|
|
@ -132,33 +203,5 @@ object InterstellarCluster {
|
|||
* @see `BuildingInfoUpdateMessage`
|
||||
* @param zone_num the zone number to request building map updates for
|
||||
*/
|
||||
final case class ZoneMapUpdate(zone_num: Int)
|
||||
final case class ZoneMapUpdate(zone_num : Int)
|
||||
}
|
||||
|
||||
/*
|
||||
// List[Building] --> List[List[(Amenity, Building)]] --> List[(SpawnTube*, Building)]
|
||||
zone.LocalLattice.Buildings.values
|
||||
.filter(_.Faction == player.Faction)
|
||||
.map(building => { building.Amenities.map { _ -> building } })
|
||||
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
|
||||
*/
|
||||
|
||||
/*
|
||||
zone.Buildings.values.filter(building => {
|
||||
(
|
||||
if(spawn_zone == 6) { Set(StructureType.Tower) }
|
||||
else if(spawn_zone == 7) { Set(StructureType.Facility, StructureType.Building) }
|
||||
else { Set.empty[StructureType.Value] }
|
||||
).contains(building.BuildingType) &&
|
||||
building.Amenities.exists(_.isInstanceOf[SpawnTube]) &&
|
||||
building.Faction == player.Faction &&
|
||||
building.Position != Vector3.Zero
|
||||
})
|
||||
.toSeq
|
||||
.sortBy(building => {
|
||||
Vector3.DistanceSquared(player.Position, building.Position) < Vector3.DistanceSquared(player.Position, building.Position)
|
||||
})
|
||||
.map(building => { building.Amenities.map { _ -> building } })
|
||||
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
|
||||
).headOption
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class SphereOfInfluenceActor(zone: Zone) extends Actor {
|
|||
sois = zone.Buildings
|
||||
.values
|
||||
.map { facility => (facility, facility.Definition) }
|
||||
.collect { case (facility, soi : ObjectDefinition with SphereOfInfluence) if soi.SOIRadius > 0 =>
|
||||
.collect { case (facility, soi) if soi.SOIRadius > 0 =>
|
||||
(facility, soi.SOIRadius * soi.SOIRadius)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
private var projector : ActorRef = ActorRef.noSender
|
||||
/** */
|
||||
private var hotspots : ListBuffer[HotSpotInfo] = ListBuffer[HotSpotInfo]()
|
||||
/** */
|
||||
private val hotspotHistory : ListBuffer[HotSpotInfo] = ListBuffer[HotSpotInfo]()
|
||||
/** calculate a approximated coordinate from a raw input coordinate */
|
||||
private var hotspotCoordinateFunc : Vector3=>Vector3 = Zone.HotSpot.Rules.OneToOne
|
||||
/** calculate a duration from a given interaction's participants */
|
||||
|
|
@ -128,7 +130,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
deployables = context.actorOf(Props(classOf[ZoneDeployableActor], this, constructions), s"$Id-deployables")
|
||||
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
|
||||
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
|
||||
projector = context.actorOf(Props(classOf[ZoneHotSpotProjector], this), s"$Id-hotpots")
|
||||
projector = context.actorOf(Props(classOf[ZoneHotSpotDisplay], this, hotspots, 15 seconds, hotspotHistory, 60 seconds), s"$Id-hotspots")
|
||||
soi = context.actorOf(Props(classOf[SphereOfInfluenceActor], this), s"$Id-soi")
|
||||
|
||||
avatarEvents = context.actorOf(Props(classOf[AvatarService], this), s"$Id-avatar-events")
|
||||
|
|
@ -504,25 +506,21 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
|
||||
def Activity : ActorRef = projector
|
||||
|
||||
def HotSpots : List[HotSpotInfo] = hotspots toList
|
||||
def HotSpots : List[HotSpotInfo] = hotSpotListDuplicate(hotspots).toList
|
||||
|
||||
def HotSpots_=(spots : Seq[HotSpotInfo]) : List[HotSpotInfo] = {
|
||||
hotspots.clear
|
||||
hotspots ++= spots
|
||||
HotSpots
|
||||
}
|
||||
def HotSpotData : List[HotSpotInfo] = hotSpotListDuplicate(hotspotHistory).toList
|
||||
|
||||
def TryHotSpot(displayLoc : Vector3) : HotSpotInfo = {
|
||||
hotspots.find(spot => spot.DisplayLocation == displayLoc) match {
|
||||
case Some(spot) =>
|
||||
//hotspot already exists
|
||||
spot
|
||||
case None =>
|
||||
//insert new hotspot
|
||||
val spot = new HotSpotInfo(displayLoc)
|
||||
hotspots += spot
|
||||
spot
|
||||
private def hotSpotListDuplicate(data : ListBuffer[HotSpotInfo]) : ListBuffer[HotSpotInfo] = {
|
||||
val out = data map { info =>
|
||||
val outData = new HotSpotInfo(info.DisplayLocation)
|
||||
info.Activity.foreach { case (faction, report) =>
|
||||
val doctoredReport = outData.Activity(faction)
|
||||
doctoredReport.ReportOld(report.Heat)
|
||||
doctoredReport.SetLastReport(report.LastReport)
|
||||
}
|
||||
outData
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
def HotSpotCoordinateFunction : Vector3=>Vector3 = hotspotCoordinateFunc
|
||||
|
|
@ -678,12 +676,45 @@ object Zone {
|
|||
/**
|
||||
* Message requesting that the current zone determine where a `player` can spawn.
|
||||
* @param zone_number this zone's numeric identifier
|
||||
* @param player the `Player` object
|
||||
* @param position the locality that the result should adhere
|
||||
* @param faction which empire's spawn options should be available
|
||||
* @param spawn_group the category of spawn points the request wants searched
|
||||
*/
|
||||
final case class RequestSpawnPoint(zone_number : Int, player : Player, spawn_group : Int)
|
||||
final case class RequestSpawnPoint(zone_number : Int, position : Vector3, faction : PlanetSideEmpire.Value, spawn_group : Int)
|
||||
|
||||
object RequestSpawnPoint {
|
||||
/**
|
||||
* Overloaded constructor for `RequestSpawnPoint`.
|
||||
* @param zone_number this zone's numeric identifier
|
||||
* @param player the `Player` object
|
||||
* @param spawn_group the category of spawn points the request wants searched
|
||||
*/
|
||||
def apply(zone_number : Int, player : Player, spawn_group : Int) : RequestSpawnPoint = {
|
||||
RequestSpawnPoint(zone_number, player.Position, player.Faction, spawn_group)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Message requesting a particular spawn point in the current zone.
|
||||
* @param zone_number this zone's numeric identifier
|
||||
* @param position the locality that the result should adhere
|
||||
* @param faction which empire's spawn options should be available
|
||||
* @param target the identifier of the spawn object
|
||||
*/
|
||||
final case class RequestSpecificSpawnPoint(zone_number : Int, position : Vector3, faction : PlanetSideEmpire.Value, target : PlanetSideGUID)
|
||||
|
||||
object RequestSpecificSpawnPoint {
|
||||
/**
|
||||
* Overloaded constructor for `RequestSpecificSpawnPoint`.
|
||||
* @param zone_number this zone's numeric identifier
|
||||
* @param player the `Player` object
|
||||
* @param target the identifier of the spawn object
|
||||
*/
|
||||
def apply(zone_number : Int, player : Player, target : PlanetSideGUID) : RequestSpecificSpawnPoint = {
|
||||
RequestSpecificSpawnPoint(zone_number, player.Position, player.Faction, target)
|
||||
}
|
||||
}
|
||||
|
||||
final case class RequestSpecificSpawnPoint(zone_number : Int, player : Player, target : PlanetSideGUID)
|
||||
/**
|
||||
* Message that returns a discovered spawn point to a request source.
|
||||
* @param zone_id the zone's text identifier
|
||||
|
|
|
|||
|
|
@ -81,66 +81,9 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
zone.Activity forward msg
|
||||
|
||||
//own
|
||||
case Zone.Lattice.RequestSpawnPoint(zone_number, player, spawn_group) =>
|
||||
case Zone.Lattice.RequestSpawnPoint(zone_number, position, faction, spawn_group) =>
|
||||
if(zone_number == zone.Number) {
|
||||
val playerPosition = player.Position.xy
|
||||
(
|
||||
if(spawn_group == 2) {
|
||||
//ams
|
||||
zone.Vehicles
|
||||
.filter(veh =>
|
||||
veh.Definition == GlobalDefinitions.ams &&
|
||||
!veh.Destroyed &&
|
||||
veh.DeploymentState == DriveState.Deployed &&
|
||||
veh.Faction == player.Faction
|
||||
)
|
||||
.sortBy(veh => Vector3.DistanceSquared(playerPosition, veh.Position.xy))
|
||||
.flatMap(veh => veh.Utilities.values.filter(util => util.UtilType == UtilityType.ams_respawn_tube))
|
||||
.headOption match {
|
||||
case None =>
|
||||
None
|
||||
case Some(util) =>
|
||||
Some(List(util().asInstanceOf[SpawnTube]))
|
||||
}
|
||||
}
|
||||
else {
|
||||
//facilities, towers, and buildings
|
||||
val buildingTypeSet = if(spawn_group == 0) {
|
||||
Set(StructureType.Facility, StructureType.Tower, StructureType.Building)
|
||||
}
|
||||
else if(spawn_group == 6) {
|
||||
Set(StructureType.Tower)
|
||||
}
|
||||
else if(spawn_group == 7) {
|
||||
Set(StructureType.Facility, StructureType.Building)
|
||||
}
|
||||
else if(spawn_group == 12) {
|
||||
Set(StructureType.WarpGate)
|
||||
}
|
||||
else {
|
||||
Set.empty[StructureType.Value]
|
||||
}
|
||||
zone.SpawnGroups()
|
||||
.filter({ case (building, tubes) =>
|
||||
buildingTypeSet.contains(building.BuildingType) && (building match {
|
||||
case wg : WarpGate =>
|
||||
building.Faction == player.Faction || building.Faction == PlanetSideEmpire.NEUTRAL || wg.Broadcast
|
||||
case _ =>
|
||||
building.Faction == player.Faction && !tubes.forall(sp => sp.Offline)
|
||||
})
|
||||
})
|
||||
.toSeq
|
||||
.sortBy({ case (building, _) =>
|
||||
Vector3.DistanceSquared(playerPosition, building.Position.xy)
|
||||
})
|
||||
.headOption match {
|
||||
case None | Some((_, Nil)) =>
|
||||
None
|
||||
case Some((_, tubes)) =>
|
||||
Some(tubes)
|
||||
}
|
||||
}
|
||||
) match {
|
||||
ZoneActor.FindLocalSpawnPointsInZone(zone, position, faction, spawn_group) match {
|
||||
case Some(List(tube)) =>
|
||||
sender ! Zone.Lattice.SpawnPoint(zone.Id, tube)
|
||||
|
||||
|
|
@ -158,13 +101,13 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
|
||||
}
|
||||
|
||||
case Zone.Lattice.RequestSpecificSpawnPoint(zone_number, player, target) =>
|
||||
case Zone.Lattice.RequestSpecificSpawnPoint(zone_number, _, faction, target) =>
|
||||
if(zone_number == zone.Number) {
|
||||
//is our spawn point some other privileged vehicle?
|
||||
zone.Vehicles.collectFirst({
|
||||
case vehicle : SpawnPoint if vehicle.Faction == player.Faction && vehicle.GUID == target =>
|
||||
case vehicle : SpawnPoint if vehicle.Faction == faction && vehicle.GUID == target =>
|
||||
Some(vehicle) //the vehicle itself is the spawn point
|
||||
case vehicle if vehicle.Faction == player.Faction && vehicle.GUID == target =>
|
||||
case vehicle if vehicle.Faction == faction && vehicle.GUID == target =>
|
||||
vehicle.Utilities.values.find {
|
||||
util =>
|
||||
util().isInstanceOf[SpawnPoint]
|
||||
|
|
@ -180,9 +123,9 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
case(building, _) =>
|
||||
building match {
|
||||
case wg : WarpGate =>
|
||||
building.Faction == player.Faction || building.Faction == PlanetSideEmpire.NEUTRAL || wg.Broadcast
|
||||
building.Faction == faction || building.Faction == PlanetSideEmpire.NEUTRAL || wg.Broadcast
|
||||
case _ =>
|
||||
building.Faction == player.Faction
|
||||
building.Faction == faction
|
||||
}
|
||||
}
|
||||
friendlySpawnGroups.collectFirst({
|
||||
|
|
@ -273,6 +216,72 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
}
|
||||
|
||||
object ZoneActor {
|
||||
/**
|
||||
* na
|
||||
* @param zone na
|
||||
* @param position na
|
||||
* @param faction na
|
||||
* @param spawn_group na
|
||||
* @return na
|
||||
*/
|
||||
def FindLocalSpawnPointsInZone(zone : Zone, position : Vector3, faction : PlanetSideEmpire.Value, spawn_group : Int) : Option[List[SpawnPoint]] = {
|
||||
val playerPosition = position.xy
|
||||
if(spawn_group == 2) {
|
||||
//ams
|
||||
zone.Vehicles
|
||||
.filter(veh =>
|
||||
veh.Definition == GlobalDefinitions.ams &&
|
||||
veh.DeploymentState == DriveState.Deployed &&
|
||||
veh.Faction == faction
|
||||
)
|
||||
.sortBy(veh => Vector3.DistanceSquared(playerPosition, veh.Position.xy))
|
||||
.flatMap(veh => veh.Utilities.values.filter(util => util.UtilType == UtilityType.ams_respawn_tube))
|
||||
.headOption match {
|
||||
case None =>
|
||||
None
|
||||
case Some(util) =>
|
||||
Some(List(util().asInstanceOf[SpawnTube]))
|
||||
}
|
||||
}
|
||||
else {
|
||||
//facilities, towers, and buildings
|
||||
val buildingTypeSet = if(spawn_group == 0) {
|
||||
Set(StructureType.Facility, StructureType.Tower, StructureType.Building)
|
||||
}
|
||||
else if(spawn_group == 6) {
|
||||
Set(StructureType.Tower)
|
||||
}
|
||||
else if(spawn_group == 7) {
|
||||
Set(StructureType.Facility, StructureType.Building)
|
||||
}
|
||||
else if(spawn_group == 12) {
|
||||
Set(StructureType.WarpGate)
|
||||
}
|
||||
else {
|
||||
Set.empty[StructureType.Value]
|
||||
}
|
||||
zone.SpawnGroups()
|
||||
.filter({ case (building, _) =>
|
||||
buildingTypeSet.contains(building.BuildingType) && (building match {
|
||||
case wg : WarpGate =>
|
||||
building.Faction == faction || building.Faction == PlanetSideEmpire.NEUTRAL || wg.Broadcast
|
||||
case _ =>
|
||||
building.Faction == faction
|
||||
})
|
||||
})
|
||||
.toSeq
|
||||
.sortBy({ case (building, _) =>
|
||||
Vector3.DistanceSquared(playerPosition, building.Position.xy)
|
||||
})
|
||||
.headOption match {
|
||||
case None | Some((_, Nil)) =>
|
||||
None
|
||||
case Some((_, tubes)) =>
|
||||
Some(tubes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover an object from a collection and perform any number of validating tests upon it.
|
||||
* If the object fails any tests, log an error.
|
||||
|
|
|
|||
|
|
@ -1,26 +1,67 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
||||
import net.psforever.objects.DefaultCancellable
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
import services.ServiceManager
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Manage hotspot information for a given zone,
|
||||
* keeping track of aggressive faction interactions,
|
||||
* and maintaining the visibility state of the hotspots that alert of the location of that activity.
|
||||
* @param zone the zone
|
||||
* and maintaining the visibility state of the hotspots that alert of the location of that activity.<br>
|
||||
* <br>
|
||||
* Initializes two internal devices to manage the hotspot activity reported by the zone.
|
||||
* The first device - "projector" - keeps track of any hotspots that are currently being displayed on the zone map.
|
||||
* The second device - "backup" - is designed to maintain a much longer record of the same hostpot activity
|
||||
* that was displayed by the projector.
|
||||
* Messages sent to this device are sent automatically to each internal device.
|
||||
* The internal devices do not have to be messaged separately.
|
||||
* @see `ZoneHotSpotProjector`
|
||||
* @see `ZoneHotSpotHistory`
|
||||
* @param zone the zone whose map serves as the "screen" for the hotspot data
|
||||
* @param outputList an external list used for storing displayed activity hotspots
|
||||
* @param outputBlanking the period of decay time before hotspot information is forgotten
|
||||
* @param dataList an external list used for storing activity for prolonged periods of time
|
||||
* @param dataBlanking the period of decay time before prolonged activity information is forgotten
|
||||
*/
|
||||
class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
||||
class ZoneHotSpotDisplay(zone : Zone,
|
||||
outputList : ListBuffer[HotSpotInfo],
|
||||
outputBlanking : FiniteDuration,
|
||||
dataList : ListBuffer[HotSpotInfo],
|
||||
dataBlanking : FiniteDuration) extends Actor {
|
||||
val projector = context.actorOf(Props(classOf[ZoneHotSpotProjector], zone, outputList, outputBlanking), s"${zone.Id}-hotspot-projector")
|
||||
val backup = context.actorOf(Props(classOf[ZoneHotSpotHistory], zone, dataList, dataBlanking), s"${zone.Id}-hotspot-backup")
|
||||
|
||||
def receive : Receive = {
|
||||
case _ if sender == projector || sender == backup => ; //catch and disrupt cyclic messaging paths
|
||||
case msg =>
|
||||
projector ! msg
|
||||
backup ! msg
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage hotspot information for a given zone,
|
||||
* keeping track of aggressive faction interactions,
|
||||
* and maintaining the visibility state of the hotspots that alert of the location of that activity.
|
||||
* One of the internal devices controlled by the `ZoneHotSpotDisplay`,
|
||||
* this is the "projector" component that actually displays hotspots onto the zone's map.
|
||||
* @see `ZoneHotSpotDisplay`
|
||||
* @param zone the zone
|
||||
* @param hotspots the data structure of hot spot information that this projector will be leveraging
|
||||
* @param blankingTime how long to wait in between blanking periods
|
||||
*/
|
||||
class ZoneHotSpotProjector(zone : Zone, hotspots : ListBuffer[HotSpotInfo], blankingTime : FiniteDuration) extends Actor {
|
||||
/** a hook for the `GalaxyService` used to broadcast messages */
|
||||
private var galaxy : ActorRef = ActorRef.noSender
|
||||
var galaxy : ActorRef = ActorRef.noSender
|
||||
/** the timer for the blanking process */
|
||||
private var blanking : Cancellable = DefaultCancellable.obj
|
||||
var blanking : Cancellable = DefaultCancellable.obj
|
||||
/** how long to wait in between blanking periods while hotspots decay */
|
||||
private val blankingDelay : FiniteDuration = 15 seconds
|
||||
var blankingDelay : FiniteDuration = blankingTime
|
||||
|
||||
private[this] val log = org.log4s.getLogger(s"${zone.Id.capitalize}HotSpotProjector")
|
||||
|
||||
|
|
@ -88,7 +129,7 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
case ZoneHotSpotProjector.UpdateDurationFunction() =>
|
||||
blanking.cancel
|
||||
UpdateDurationFunction()
|
||||
UpdateHotSpots(PlanetSideEmpire.values, zone.HotSpots)
|
||||
UpdateHotSpots(PlanetSideEmpire.values, hotspots)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
blanking = context.system.scheduler.scheduleOnce(blankingDelay, self, ZoneHotSpotProjector.BlankingPhase())
|
||||
|
||||
|
|
@ -97,7 +138,7 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
//this is different from the many individual activity locations that contributed to that `DisplayLocation`
|
||||
blanking.cancel
|
||||
UpdateMappingFunction()
|
||||
UpdateHotSpots(PlanetSideEmpire.values, zone.HotSpots)
|
||||
UpdateHotSpots(PlanetSideEmpire.values, hotspots)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
blanking = context.system.scheduler.scheduleOnce(blankingDelay, self, ZoneHotSpotProjector.BlankingPhase())
|
||||
|
||||
|
|
@ -105,10 +146,10 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
log.trace(s"received information about activity in ${zone.Id}@$location")
|
||||
val defenderFaction = defender.Faction
|
||||
val attackerFaction = attacker.Faction
|
||||
val noPriorHotSpots = zone.HotSpots.isEmpty
|
||||
val noPriorHotSpots = hotspots.isEmpty
|
||||
val duration = zone.HotSpotTimeFunction(defender, attacker)
|
||||
if(duration.toNanos > 0) {
|
||||
val hotspot = zone.TryHotSpot( zone.HotSpotCoordinateFunction(location) )
|
||||
val hotspot = TryHotSpot( zone.HotSpotCoordinateFunction(location) )
|
||||
log.trace(s"updating activity status for ${zone.Id} hotspot x=${hotspot.DisplayLocation.x} y=${hotspot.DisplayLocation.y}")
|
||||
val noPriorActivity = !(hotspot.ActivityBy(defenderFaction) && hotspot.ActivityBy(attackerFaction))
|
||||
//update the activity report for these factions
|
||||
|
|
@ -123,7 +164,7 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
}
|
||||
//if the level of activity changed for one of the participants or the number of hotspots was zero
|
||||
if(noPriorActivity || noPriorHotSpots) {
|
||||
UpdateHotSpots(affectedFactions, zone.HotSpots)
|
||||
UpdateHotSpots(affectedFactions, hotspots)
|
||||
if(noPriorHotSpots) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
blanking.cancel
|
||||
|
|
@ -134,13 +175,13 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
|
||||
case Zone.HotSpot.UpdateNow =>
|
||||
log.trace(s"asked to update for zone ${zone.Id} without a blanking period or new activity")
|
||||
UpdateHotSpots(PlanetSideEmpire.values, zone.HotSpots)
|
||||
UpdateHotSpots(PlanetSideEmpire.values, hotspots)
|
||||
|
||||
case ZoneHotSpotProjector.BlankingPhase() | Zone.HotSpot.Cleanup() =>
|
||||
blanking.cancel
|
||||
val curr : Long = System.nanoTime
|
||||
//blanking dated activity reports
|
||||
val changed = zone.HotSpots.flatMap(spot => {
|
||||
val changed = hotspots.flatMap(spot => {
|
||||
spot.Activity.collect {
|
||||
case (b, a) if a.LastReport + a.Duration.toNanos <= curr =>
|
||||
a.Clear() //this faction has no more activity in this sector
|
||||
|
|
@ -148,7 +189,7 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
}
|
||||
})
|
||||
//collect and re-assign still-relevant hotspots
|
||||
val spots = zone.HotSpots.filter(spot => {
|
||||
val spots = hotspots.filter(spot => {
|
||||
spot.Activity
|
||||
.values
|
||||
.collect {
|
||||
|
|
@ -157,9 +198,11 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
}
|
||||
.foldLeft(false)(_ || _)
|
||||
})
|
||||
val changesOnMap = zone.HotSpots.size - spots.size
|
||||
log.trace(s"blanking out $changesOnMap hotspots from zone ${zone.Id}; ${spots.size} remain active")
|
||||
zone.HotSpots = spots
|
||||
val newSize = spots.size
|
||||
val changesOnMap = hotspots.size - newSize
|
||||
log.trace(s"blanking out $changesOnMap hotspots from zone ${zone.Id}; $newSize remain active")
|
||||
hotspots.clear
|
||||
hotspots.insertAll(0, spots)
|
||||
//other hotspots still need to be blanked later
|
||||
if(spots.nonEmpty) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -174,18 +217,38 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
case Zone.HotSpot.ClearAll() =>
|
||||
log.trace(s"blanking out all hotspots from zone ${zone.Id} immediately")
|
||||
blanking.cancel
|
||||
zone.HotSpots = Nil
|
||||
hotspots.clear()
|
||||
UpdateHotSpots(PlanetSideEmpire.values, Nil)
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a hotspot location with a data structure for keeping track of activity information,
|
||||
* either an existing structure or one that was created in the list of activity data for this location.
|
||||
* @see `HotSpotInfo`
|
||||
* @param displayLoc the location for the hotpot that was normalized by the coordinate mapping function
|
||||
* @return the hotspot data that corresponds to this location
|
||||
*/
|
||||
def TryHotSpot(displayLoc : Vector3) : HotSpotInfo = {
|
||||
hotspots.find(spot => spot.DisplayLocation == displayLoc) match {
|
||||
case Some(spot) =>
|
||||
//hotspot already exists
|
||||
spot
|
||||
case None =>
|
||||
//insert new hotspot
|
||||
val spot = new HotSpotInfo(displayLoc)
|
||||
hotspots += spot
|
||||
spot
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a new functionality for determining how long hotspots remain active.
|
||||
* Recalculate all current hotspot information.
|
||||
*/
|
||||
def UpdateDurationFunction(): Unit = {
|
||||
zone.HotSpots.foreach { spot =>
|
||||
hotspots.foreach { spot =>
|
||||
spot.Activity.values.foreach { report =>
|
||||
val heat = report.Heat
|
||||
report.Clear()
|
||||
|
|
@ -193,7 +256,7 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
report.Duration = 0L
|
||||
}
|
||||
}
|
||||
log.trace(s"new duration remapping function provided; reloading ${zone.HotSpots.size} hotspots for one blanking phase")
|
||||
log.trace(s"new duration remapping function provided; reloading ${hotspots.size} hotspots for one blanking phase")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -201,7 +264,7 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
* Recalculate all current hotspot information.
|
||||
*/
|
||||
def UpdateMappingFunction() : Unit = {
|
||||
val redoneSpots = zone.HotSpots.map { spot =>
|
||||
val redoneSpots = hotspots.map { spot =>
|
||||
val newSpot = new HotSpotInfo( zone.HotSpotCoordinateFunction(spot.DisplayLocation) )
|
||||
PlanetSideEmpire.values.foreach { faction =>
|
||||
if(spot.ActivityBy(faction)) {
|
||||
|
|
@ -211,8 +274,9 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
}
|
||||
newSpot
|
||||
}
|
||||
log.trace(s"new coordinate remapping function provided; updating ${redoneSpots.size} hotspots")
|
||||
zone.HotSpots = redoneSpots
|
||||
log.trace(s"new coordinate remapping function provided; updating $redoneSpots.size hotspots")
|
||||
hotspots.clear()
|
||||
hotspots.insertAll(0, redoneSpots)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -224,21 +288,38 @@ class ZoneHotSpotProjector(zone : Zone) extends Actor {
|
|||
* if empty or contains no information for a selected group,
|
||||
* that group's hotspots will be eliminated (blanked) as a result
|
||||
*/
|
||||
def UpdateHotSpots(affectedFactions : Iterable[PlanetSideEmpire.Value], hotSpotInfos : List[HotSpotInfo]) : Unit = {
|
||||
def UpdateHotSpots(affectedFactions : Iterable[PlanetSideEmpire.Value], hotSpotInfos : Iterable[HotSpotInfo]) : Unit = {
|
||||
val zoneNumber = zone.Number
|
||||
val hotSpotInfoList = hotSpotInfos.toList
|
||||
affectedFactions.foreach(faction =>
|
||||
galaxy ! Zone.HotSpot.Update(
|
||||
faction,
|
||||
zoneNumber,
|
||||
1,
|
||||
ZoneHotSpotProjector.SpecificHotSpotInfo(faction, hotSpotInfos)
|
||||
ZoneHotSpotProjector.SpecificHotSpotInfo(faction, hotSpotInfoList)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def CreateHotSpotUpdate(faction : PlanetSideEmpire.Value, hotSpotInfos : List[HotSpotInfo]) : List[HotSpotInfo] = {
|
||||
Nil
|
||||
}
|
||||
/**
|
||||
* Manage hotspot information for a given zone,
|
||||
* keeping track of aggressive faction interactions,
|
||||
* and maintaining the visibility state of the hotspots that alert of the location of that activity.
|
||||
* One of the internal devices controlled by the `ZoneHotSpotDisplay`,
|
||||
* this is the "backup" component that is intended to retain reported activity for a longer period of time.
|
||||
* @see `ZoneHotSpotDisplay`
|
||||
* @see `ZoneHotSpotProjector`
|
||||
* @param zone the zone
|
||||
* @param hotspots the data structure of hot spot information that this projector will be leveraging
|
||||
* @param blankingTime how long to wait in between blanking periods
|
||||
*/
|
||||
class ZoneHotSpotHistory(zone : Zone, hotspots : ListBuffer[HotSpotInfo], blankingTime : FiniteDuration) extends ZoneHotSpotProjector(zone, hotspots, blankingTime) {
|
||||
/* the galaxy service is unnecessary */
|
||||
override def preStart() : Unit = { context.become(Established) }
|
||||
/* this component does not actually the visible hotspots
|
||||
* a duplicate of the projector device otherwise */
|
||||
override def UpdateHotSpots(affectedFactions : Iterable[PlanetSideEmpire.Value], hotSpotInfos : Iterable[HotSpotInfo]) : Unit = { }
|
||||
}
|
||||
|
||||
object ZoneHotSpotProjector {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class ZoneMap(private val name : String) {
|
|||
private var lattice: Set[(String, String)] = Set()
|
||||
private var checksum : Long = 0
|
||||
private var zipLinePaths : List[ZipLinePath] = List()
|
||||
private var cavern : Boolean = false
|
||||
|
||||
def Name : String = name
|
||||
|
||||
|
|
@ -141,4 +142,11 @@ class ZoneMap(private val name : String) {
|
|||
def LatticeLink(source : String, target: String) : Unit = {
|
||||
lattice = lattice ++ Set((source, target))
|
||||
}
|
||||
|
||||
def Cavern : Boolean = cavern
|
||||
|
||||
def Cavern_=(cave : Boolean) : Boolean = {
|
||||
cavern = cave
|
||||
Cavern
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package net.psforever.objects.zones
|
||||
|
||||
import net.psforever.objects.SpawnPoint
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
|
||||
object Zoning {
|
||||
object Method extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
None,
|
||||
InstantAction,
|
||||
Recall
|
||||
= Value
|
||||
}
|
||||
|
||||
object Status extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
None,
|
||||
Request,
|
||||
Countdown
|
||||
= Value
|
||||
}
|
||||
|
||||
object Time {
|
||||
sealed case class TimeType(id : Int, descriptor : String)
|
||||
|
||||
final val Immediate = TimeType(0, "Immediate")
|
||||
final val Friendly = TimeType(10, "Friendly")
|
||||
final val Sanctuary = TimeType(10, "Sanctuary")
|
||||
final val Neutral = TimeType(20, "Neutral")
|
||||
final val None = TimeType(20, "None")
|
||||
final val Enemy = TimeType(30, "Enemy")
|
||||
}
|
||||
|
||||
object InstantAction {
|
||||
final case class Request(faction : PlanetSideEmpire.Value)
|
||||
|
||||
final case class Located(zone : Zone, hotspot : Vector3, spawn_point : SpawnPoint)
|
||||
|
||||
final case class NotLocated()
|
||||
}
|
||||
|
||||
object Recall {
|
||||
final case class Request(faction : PlanetSideEmpire.Value, sanctuary_id : String)
|
||||
|
||||
final case class Located(zone : Zone, spawn_point : SpawnPoint)
|
||||
|
||||
final case class Denied(reason : String)
|
||||
}
|
||||
}
|
||||
|
|
@ -440,7 +440,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x66 => game.WeaponJammedMessage.decode
|
||||
case 0x67 => noDecoder(LinkDeadAwarenessMsg)
|
||||
// 0x68
|
||||
case 0x68 => noDecoder(DroppodFreefallingMessage)
|
||||
case 0x68 => game.DroppodFreefallingMessage.decode
|
||||
case 0x69 => game.AvatarFirstTimeEventMessage.decode
|
||||
case 0x6a => noDecoder(AggravatedDamageMessage)
|
||||
case 0x6b => game.TriggerSoundMessage.decode
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import net.psforever.types.{Angular, PlanetSideGUID, Vector3}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
final case class DroppodFreefallingMessage(guid : PlanetSideGUID,
|
||||
pos : Vector3,
|
||||
vel : Vector3,
|
||||
pos2 : Vector3,
|
||||
orientation1 : Vector3,
|
||||
orientation2 : Vector3)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = DroppodFreefallingMessage
|
||||
def opcode = GamePacketOpcode.DroppodFreefallingMessage
|
||||
def encode = DroppodFreefallingMessage.encode(this)
|
||||
}
|
||||
|
||||
object DroppodFreefallingMessage extends Marshallable[DroppodFreefallingMessage] {
|
||||
implicit val codec : Codec[DroppodFreefallingMessage] = (
|
||||
("guid" | PlanetSideGUID.codec) ::
|
||||
("pos" | Vector3.codec_float) ::
|
||||
("vel" | Vector3.codec_float) ::
|
||||
("pos2" | Vector3.codec_float) ::
|
||||
("unkA" | Angular.codec_roll) ::
|
||||
("unkB" | Angular.codec_pitch) ::
|
||||
("unkC" | Angular.codec_yaw()) ::
|
||||
("unkD" | Angular.codec_roll) ::
|
||||
("unkE" | Angular.codec_pitch) ::
|
||||
("unkF" | Angular.codec_yaw())
|
||||
).xmap[DroppodFreefallingMessage](
|
||||
{
|
||||
case guid :: pos :: vel :: pos2 :: uA :: uB :: uC :: uD :: uE :: uF :: HNil =>
|
||||
DroppodFreefallingMessage(guid, pos, vel, pos2, Vector3(uA, uB, uC), Vector3(uD, uE, uF))
|
||||
},
|
||||
{
|
||||
case DroppodFreefallingMessage(guid, pos, vel, pos2, Vector3(uA, uB, uC), Vector3(uD, uE, uF)) =>
|
||||
guid :: pos :: vel :: pos2 :: uA :: uB :: uC :: uD :: uE :: uF :: HNil
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -276,7 +276,7 @@ object Vector3 {
|
|||
}
|
||||
|
||||
/**
|
||||
* Perform the x-axis rotation of a `Vector3` element where the angle of rotation is assumed in degrees.
|
||||
* Perform the y-axis rotation of a `Vector3` element where the angle of rotation is assumed in degrees.
|
||||
* @see `Vector3.Ry(Vector3, Double)`
|
||||
* @param vec a mathematical vector representing direction
|
||||
* @param ang a rotation angle, in degrees
|
||||
|
|
@ -302,7 +302,7 @@ object Vector3 {
|
|||
}
|
||||
|
||||
/**
|
||||
* Perform the x-axis rotation of a `Vector3` element where the angle of rotation is assumed in degrees.
|
||||
* Perform the z-axis rotation of a `Vector3` element where the angle of rotation is assumed in degrees.
|
||||
* @see `Vector3.Rz(Vector3, Double)`
|
||||
* @param vec a mathematical vector representing direction
|
||||
* @param ang a rotation angle, in degrees
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||
import scodec.bits._
|
||||
|
||||
class DroppodFreefallingMessageTest extends Specification {
|
||||
val string = hex"68 220e 00e0b245 00c06145 00a08744 00000000 00000000 ffff79c4 0740b245 22c66145 00608144 00 67 3f 00 00 3f"
|
||||
|
||||
"DroppodFreefallingMessage" should {
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case DroppodFreefallingMessage(guid, pos, vel, pos2, orientation1, orientation2) =>
|
||||
guid mustEqual PlanetSideGUID(3618)
|
||||
pos mustEqual Vector3(5724, 3612, 1085)
|
||||
vel mustEqual Vector3(0, 0, -999.99994f)
|
||||
pos2 mustEqual Vector3(5704.0034f, 3612.3833f, 1035.0f)
|
||||
orientation1 mustEqual Vector3(0, 70.3125f, 272.8125f)
|
||||
orientation2 mustEqual Vector3(0, 0, 272.8125f)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = DroppodFreefallingMessage(
|
||||
PlanetSideGUID(3618),
|
||||
Vector3(5724, 3612, 1085),
|
||||
Vector3(0, 0, -999.99994f),
|
||||
Vector3(5704.0034f, 3612.3833f, 1035.0f),
|
||||
Vector3(0, 70.3125f, 272.8125f), Vector3(0, 0, 272.8125f))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ class ImplantTerminalMechObjectBuilderTest extends ActorTest {
|
|||
"Implant terminal mech object" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, ImplantTerminalMech.Constructor), hub), "mech")
|
||||
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, ImplantTerminalMech.Constructor(Vector3.Zero)), hub), "mech")
|
||||
actor ! "!"
|
||||
|
||||
val reply = receiveOne(Duration.create(1000, "ms"))
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,7 @@ import net.psforever.types.Vector3
|
|||
object Ugd01 { // Supai
|
||||
val ZoneMap = new ZoneMap("ugd01") {
|
||||
Scale = MapScale.Dim2560
|
||||
Cavern = true
|
||||
Checksum = 3405929729L
|
||||
|
||||
Building10140()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import net.psforever.types.Vector3
|
|||
object Ugd02 { // Hunhau
|
||||
val ZoneMap = new ZoneMap("ugd02") {
|
||||
Scale = MapScale.Dim2560
|
||||
Cavern = true
|
||||
Checksum = 2702486449L
|
||||
|
||||
Building10093()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import net.psforever.types.Vector3
|
|||
object Ugd03 { // Adlivun
|
||||
val ZoneMap = new ZoneMap("ugd03") {
|
||||
Scale = MapScale.Dim2048
|
||||
Cavern = true
|
||||
Checksum = 1673539651L
|
||||
|
||||
Building10020()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import net.psforever.types.Vector3
|
|||
object Ugd04 { // Byblos
|
||||
val ZoneMap = new ZoneMap("ugd04") {
|
||||
Scale = MapScale.Dim2048
|
||||
Cavern = true
|
||||
Checksum = 3797992164L
|
||||
|
||||
Building10076()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import net.psforever.types.Vector3
|
|||
object Ugd05 { // Annwn
|
||||
val ZoneMap = new ZoneMap("ugd05") {
|
||||
Scale = MapScale.Dim2048
|
||||
Cavern = true
|
||||
Checksum = 1769572498L
|
||||
|
||||
Building10116()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import net.psforever.types.Vector3
|
|||
object Ugd06 { // Drugaskan
|
||||
val ZoneMap = new ZoneMap("ugd06") {
|
||||
Scale = MapScale.Dim2560
|
||||
Cavern = true
|
||||
Checksum = 4274683970L
|
||||
|
||||
Building10077()
|
||||
|
|
|
|||
Loading…
Reference in a new issue