mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 15:34:42 +00:00
commit
885387b734
22 changed files with 1095 additions and 348 deletions
|
|
@ -31,70 +31,18 @@ object GlobalDefinitions {
|
||||||
*/
|
*/
|
||||||
val avatar = new AvatarDefinition(121)
|
val avatar = new AvatarDefinition(121)
|
||||||
/*
|
/*
|
||||||
Exo-suits
|
exo-suits
|
||||||
*/
|
*/
|
||||||
val Standard = ExoSuitDefinition(ExoSuitType.Standard)
|
val Standard = ExoSuitDefinition(ExoSuitType.Standard)
|
||||||
Standard.Name = "standard"
|
|
||||||
Standard.MaxArmor = 50
|
|
||||||
Standard.InventoryScale = InventoryTile.Tile96
|
|
||||||
Standard.InventoryOffset = 6
|
|
||||||
Standard.Holster(0, EquipmentSize.Pistol)
|
|
||||||
Standard.Holster(2, EquipmentSize.Rifle)
|
|
||||||
Standard.Holster(4, EquipmentSize.Melee)
|
|
||||||
Standard.ResistanceDirectHit = 4
|
|
||||||
Standard.ResistanceSplash = 15
|
|
||||||
Standard.ResistanceAggravated = 8
|
|
||||||
|
|
||||||
val Agile = ExoSuitDefinition(ExoSuitType.Agile)
|
val Agile = ExoSuitDefinition(ExoSuitType.Agile)
|
||||||
Agile.Name = "agile"
|
|
||||||
Agile.MaxArmor = 100
|
|
||||||
Agile.InventoryScale = InventoryTile.Tile99
|
|
||||||
Agile.InventoryOffset = 6
|
|
||||||
Agile.Holster(0, EquipmentSize.Pistol)
|
|
||||||
Agile.Holster(1, EquipmentSize.Pistol)
|
|
||||||
Agile.Holster(2, EquipmentSize.Rifle)
|
|
||||||
Agile.Holster(4, EquipmentSize.Melee)
|
|
||||||
Agile.ResistanceDirectHit = 6
|
|
||||||
Agile.ResistanceSplash = 25
|
|
||||||
Agile.ResistanceAggravated = 10
|
|
||||||
|
|
||||||
val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced)
|
val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced)
|
||||||
Reinforced.Name = "reinforced"
|
|
||||||
Reinforced.Permissions = List(CertificationType.ReinforcedExoSuit)
|
|
||||||
Reinforced.MaxArmor = 200
|
|
||||||
Reinforced.InventoryScale = InventoryTile.Tile1209
|
|
||||||
Reinforced.InventoryOffset = 6
|
|
||||||
Reinforced.Holster(0, EquipmentSize.Pistol)
|
|
||||||
Reinforced.Holster(1, EquipmentSize.Pistol)
|
|
||||||
Reinforced.Holster(2, EquipmentSize.Rifle)
|
|
||||||
Reinforced.Holster(3, EquipmentSize.Rifle)
|
|
||||||
Reinforced.Holster(4, EquipmentSize.Melee)
|
|
||||||
Reinforced.ResistanceDirectHit = 10
|
|
||||||
Reinforced.ResistanceSplash = 35
|
|
||||||
Reinforced.ResistanceAggravated = 12
|
|
||||||
|
|
||||||
val Infiltration = ExoSuitDefinition(ExoSuitType.Infiltration)
|
val Infiltration = ExoSuitDefinition(ExoSuitType.Infiltration)
|
||||||
Infiltration.Name = "infiltration_suit"
|
|
||||||
Infiltration.Permissions = List(CertificationType.InfiltrationSuit)
|
|
||||||
Infiltration.MaxArmor = 0
|
|
||||||
Infiltration.InventoryScale = InventoryTile.Tile66
|
|
||||||
Infiltration.InventoryOffset = 6
|
|
||||||
Infiltration.Holster(0, EquipmentSize.Pistol)
|
|
||||||
Infiltration.Holster(4, EquipmentSize.Melee)
|
|
||||||
|
|
||||||
val MAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
|
val MAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
|
||||||
MAX.Permissions = List(CertificationType.AIMAX,CertificationType.AVMAX, CertificationType.AAMAX, CertificationType.UniMAX)
|
init_exosuit()
|
||||||
MAX.MaxArmor = 650
|
|
||||||
MAX.InventoryScale = InventoryTile.Tile1612
|
|
||||||
MAX.InventoryOffset = 6
|
|
||||||
MAX.Holster(0, EquipmentSize.Max)
|
|
||||||
MAX.Holster(4, EquipmentSize.Melee)
|
|
||||||
MAX.Subtract.Damage1 = -2
|
|
||||||
MAX.ResistanceDirectHit = 6
|
|
||||||
MAX.ResistanceSplash = 35
|
|
||||||
MAX.ResistanceAggravated = 10
|
|
||||||
MAX.Damage = StandardMaxDamage
|
|
||||||
MAX.Model = StandardResolutions.Max
|
|
||||||
/*
|
/*
|
||||||
Implants
|
Implants
|
||||||
*/
|
*/
|
||||||
|
|
@ -121,6 +69,8 @@ object GlobalDefinitions {
|
||||||
/*
|
/*
|
||||||
Projectiles
|
Projectiles
|
||||||
*/
|
*/
|
||||||
|
val no_projectile = new ProjectileDefinition(0) //also called none in ADB
|
||||||
|
|
||||||
val bullet_105mm_projectile = ProjectileDefinition(Projectiles.bullet_105mm_projectile)
|
val bullet_105mm_projectile = ProjectileDefinition(Projectiles.bullet_105mm_projectile)
|
||||||
|
|
||||||
val bullet_12mm_projectile = ProjectileDefinition(Projectiles.bullet_12mm_projectile)
|
val bullet_12mm_projectile = ProjectileDefinition(Projectiles.bullet_12mm_projectile)
|
||||||
|
|
@ -297,6 +247,8 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val oicw_projectile = ProjectileDefinition(Projectiles.oicw_projectile)
|
val oicw_projectile = ProjectileDefinition(Projectiles.oicw_projectile)
|
||||||
|
|
||||||
|
val oicw_little_buddy = ProjectileDefinition(Projectiles.oicw_little_buddy)
|
||||||
|
|
||||||
val pellet_gun_projectile = ProjectileDefinition(Projectiles.pellet_gun_projectile)
|
val pellet_gun_projectile = ProjectileDefinition(Projectiles.pellet_gun_projectile)
|
||||||
|
|
||||||
val peregrine_dual_machine_gun_projectile = ProjectileDefinition(Projectiles.peregrine_dual_machine_gun_projectile)
|
val peregrine_dual_machine_gun_projectile = ProjectileDefinition(Projectiles.peregrine_dual_machine_gun_projectile)
|
||||||
|
|
@ -628,19 +580,11 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val maelstrom = ToolDefinition(ObjectClass.maelstrom)
|
val maelstrom = ToolDefinition(ObjectClass.maelstrom)
|
||||||
|
|
||||||
val phoenix = new ToolDefinition(ObjectClass.phoenix) {
|
val phoenix = ToolDefinition(ObjectClass.phoenix) //decimator
|
||||||
override def NextFireModeIndex(index : Int) : Int = index
|
|
||||||
} //decimator
|
|
||||||
|
|
||||||
val striker = new ToolDefinition(ObjectClass.striker) {
|
val striker = ToolDefinition(ObjectClass.striker)
|
||||||
override def NextFireModeIndex(index : Int) : Int = index
|
|
||||||
DefaultFireModeIndex = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
val hunterseeker = new ToolDefinition(ObjectClass.hunterseeker) {
|
val hunterseeker = ToolDefinition(ObjectClass.hunterseeker)
|
||||||
override def NextFireModeIndex(index : Int) : Int = index
|
|
||||||
DefaultFireModeIndex = 1
|
|
||||||
} //phoenix
|
|
||||||
|
|
||||||
val lancer = ToolDefinition(ObjectClass.lancer)
|
val lancer = ToolDefinition(ObjectClass.lancer)
|
||||||
|
|
||||||
|
|
@ -807,9 +751,7 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val lightgunship_weapon_system = ToolDefinition(ObjectClass.lightgunship_weapon_system)
|
val lightgunship_weapon_system = ToolDefinition(ObjectClass.lightgunship_weapon_system)
|
||||||
|
|
||||||
val wasp_weapon_system = new ToolDefinition(ObjectClass.wasp_weapon_system) {
|
val wasp_weapon_system = new ToolDefinition(ObjectClass.wasp_weapon_system)
|
||||||
override def NextFireModeIndex(index : Int) : Int = index
|
|
||||||
}
|
|
||||||
|
|
||||||
val liberator_weapon_system = ToolDefinition(ObjectClass.liberator_weapon_system)
|
val liberator_weapon_system = ToolDefinition(ObjectClass.liberator_weapon_system)
|
||||||
|
|
||||||
|
|
@ -955,13 +897,13 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
|
val router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
|
||||||
|
|
||||||
|
//this is only treated like a deployable
|
||||||
val internal_router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
|
val internal_router_telepad_deployable = DeployableDefinition(DeployedItem.router_telepad_deployable)
|
||||||
init_deployables()
|
init_deployables()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val ams_respawn_tube = new SpawnTubeDefinition(49)
|
val ams_respawn_tube = new SpawnTubeDefinition(49)
|
||||||
|
|
||||||
val matrix_terminala = new MatrixTerminalDefinition(517)
|
val matrix_terminala = new MatrixTerminalDefinition(517)
|
||||||
|
|
@ -1947,6 +1889,11 @@ object GlobalDefinitions {
|
||||||
* Initialize `ProjectileDefinition` globals.
|
* Initialize `ProjectileDefinition` globals.
|
||||||
*/
|
*/
|
||||||
private def init_projectile() : Unit = {
|
private def init_projectile() : Unit = {
|
||||||
|
val projectileConverter : ProjectileConverter = new ProjectileConverter
|
||||||
|
|
||||||
|
no_projectile.Name = "none"
|
||||||
|
ProjectileDefinition.CalculateDerivedFields(no_projectile)
|
||||||
|
|
||||||
bullet_105mm_projectile.Name = "105mmbullet_projectile"
|
bullet_105mm_projectile.Name = "105mmbullet_projectile"
|
||||||
bullet_105mm_projectile.Damage0 = 150
|
bullet_105mm_projectile.Damage0 = 150
|
||||||
bullet_105mm_projectile.Damage1 = 300
|
bullet_105mm_projectile.Damage1 = 300
|
||||||
|
|
@ -2216,6 +2163,10 @@ object GlobalDefinitions {
|
||||||
aphelion_starfire_projectile.InitialVelocity = 45
|
aphelion_starfire_projectile.InitialVelocity = 45
|
||||||
aphelion_starfire_projectile.Lifespan = 7f
|
aphelion_starfire_projectile.Lifespan = 7f
|
||||||
aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated
|
aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||||
|
aphelion_starfire_projectile.ExistsOnRemoteClients = true
|
||||||
|
aphelion_starfire_projectile.RemoteClientData = (39577, 249) //starfire_projectile data
|
||||||
|
aphelion_starfire_projectile.AutoLock = true
|
||||||
|
aphelion_starfire_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(aphelion_starfire_projectile)
|
ProjectileDefinition.CalculateDerivedFields(aphelion_starfire_projectile)
|
||||||
|
|
||||||
bolt_projectile.Name = "bolt_projectile"
|
bolt_projectile.Name = "bolt_projectile"
|
||||||
|
|
@ -2692,6 +2643,9 @@ object GlobalDefinitions {
|
||||||
hunter_seeker_missile_projectile.ProjectileDamageType = DamageType.Splash
|
hunter_seeker_missile_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
hunter_seeker_missile_projectile.InitialVelocity = 40
|
hunter_seeker_missile_projectile.InitialVelocity = 40
|
||||||
hunter_seeker_missile_projectile.Lifespan = 6.3f
|
hunter_seeker_missile_projectile.Lifespan = 6.3f
|
||||||
|
hunter_seeker_missile_projectile.ExistsOnRemoteClients = true
|
||||||
|
hunter_seeker_missile_projectile.RemoteClientData = (39577, 201)
|
||||||
|
hunter_seeker_missile_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_projectile)
|
ProjectileDefinition.CalculateDerivedFields(hunter_seeker_missile_projectile)
|
||||||
|
|
||||||
jammer_cartridge_projectile.Name = "jammer_cartridge_projectile"
|
jammer_cartridge_projectile.Name = "jammer_cartridge_projectile"
|
||||||
|
|
@ -2977,8 +2931,24 @@ object GlobalDefinitions {
|
||||||
oicw_projectile.ProjectileDamageType = DamageType.Splash
|
oicw_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
oicw_projectile.InitialVelocity = 5
|
oicw_projectile.InitialVelocity = 5
|
||||||
oicw_projectile.Lifespan = 6.1f
|
oicw_projectile.Lifespan = 6.1f
|
||||||
|
oicw_projectile.ExistsOnRemoteClients = true
|
||||||
|
oicw_projectile.RemoteClientData = (13107, 195)
|
||||||
|
oicw_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(oicw_projectile)
|
ProjectileDefinition.CalculateDerivedFields(oicw_projectile)
|
||||||
|
|
||||||
|
oicw_little_buddy.Name = "oicw_projectile"
|
||||||
|
oicw_little_buddy.Damage0 = 75
|
||||||
|
oicw_little_buddy.Damage1 = 75
|
||||||
|
oicw_little_buddy.DamageAtEdge = 0.1f
|
||||||
|
oicw_little_buddy.DamageRadius = 7.5f
|
||||||
|
oicw_little_buddy.ProjectileDamageType = DamageType.Splash
|
||||||
|
oicw_little_buddy.InitialVelocity = 40
|
||||||
|
oicw_little_buddy.Lifespan = 0.5f
|
||||||
|
oicw_little_buddy.ExistsOnRemoteClients = false //TODO true
|
||||||
|
oicw_little_buddy.Packet = projectileConverter
|
||||||
|
//add_property oicw_little_buddy multi_stage_spawn_server_side true ...
|
||||||
|
ProjectileDefinition.CalculateDerivedFields(oicw_little_buddy)
|
||||||
|
|
||||||
pellet_gun_projectile.Name = "pellet_gun_projectile"
|
pellet_gun_projectile.Name = "pellet_gun_projectile"
|
||||||
// TODO for later, maybe : set_resource_parent pellet_gun_projectile game_objects shotgun_shell_projectile
|
// TODO for later, maybe : set_resource_parent pellet_gun_projectile game_objects shotgun_shell_projectile
|
||||||
pellet_gun_projectile.Damage0 = 12
|
pellet_gun_projectile.Damage0 = 12
|
||||||
|
|
@ -3056,6 +3026,10 @@ object GlobalDefinitions {
|
||||||
peregrine_sparrow_projectile.ProjectileDamageType = DamageType.Splash
|
peregrine_sparrow_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
peregrine_sparrow_projectile.InitialVelocity = 45
|
peregrine_sparrow_projectile.InitialVelocity = 45
|
||||||
peregrine_sparrow_projectile.Lifespan = 7.5f
|
peregrine_sparrow_projectile.Lifespan = 7.5f
|
||||||
|
peregrine_sparrow_projectile.ExistsOnRemoteClients = true
|
||||||
|
peregrine_sparrow_projectile.RemoteClientData = (13107, 187) //sparrow_projectile data
|
||||||
|
peregrine_sparrow_projectile.AutoLock = true
|
||||||
|
peregrine_sparrow_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile)
|
ProjectileDefinition.CalculateDerivedFields(peregrine_sparrow_projectile)
|
||||||
|
|
||||||
phalanx_av_projectile.Name = "phalanx_av_projectile"
|
phalanx_av_projectile.Name = "phalanx_av_projectile"
|
||||||
|
|
@ -3107,6 +3081,11 @@ object GlobalDefinitions {
|
||||||
phoenix_missile_guided_projectile.ProjectileDamageType = DamageType.Splash
|
phoenix_missile_guided_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
phoenix_missile_guided_projectile.InitialVelocity = 0
|
phoenix_missile_guided_projectile.InitialVelocity = 0
|
||||||
phoenix_missile_guided_projectile.Lifespan = 3f
|
phoenix_missile_guided_projectile.Lifespan = 3f
|
||||||
|
//not naturally a remote projectile, but being governed as one for convenience
|
||||||
|
phoenix_missile_guided_projectile.ExistsOnRemoteClients = true
|
||||||
|
phoenix_missile_guided_projectile.RemoteClientData = (0,63)
|
||||||
|
phoenix_missile_guided_projectile.Packet = projectileConverter
|
||||||
|
//
|
||||||
ProjectileDefinition.CalculateDerivedFields(phoenix_missile_guided_projectile)
|
ProjectileDefinition.CalculateDerivedFields(phoenix_missile_guided_projectile)
|
||||||
|
|
||||||
phoenix_missile_projectile.Name = "phoenix_missile_projectile"
|
phoenix_missile_projectile.Name = "phoenix_missile_projectile"
|
||||||
|
|
@ -3383,6 +3362,10 @@ object GlobalDefinitions {
|
||||||
sparrow_projectile.ProjectileDamageType = DamageType.Splash
|
sparrow_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
sparrow_projectile.InitialVelocity = 60
|
sparrow_projectile.InitialVelocity = 60
|
||||||
sparrow_projectile.Lifespan = 5.85f
|
sparrow_projectile.Lifespan = 5.85f
|
||||||
|
sparrow_projectile.ExistsOnRemoteClients = true
|
||||||
|
sparrow_projectile.RemoteClientData = (13107, 187)
|
||||||
|
sparrow_projectile.AutoLock = true
|
||||||
|
sparrow_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(sparrow_projectile)
|
ProjectileDefinition.CalculateDerivedFields(sparrow_projectile)
|
||||||
|
|
||||||
sparrow_secondary_projectile.Name = "sparrow_secondary_projectile"
|
sparrow_secondary_projectile.Name = "sparrow_secondary_projectile"
|
||||||
|
|
@ -3397,6 +3380,10 @@ object GlobalDefinitions {
|
||||||
sparrow_secondary_projectile.ProjectileDamageType = DamageType.Splash
|
sparrow_secondary_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
sparrow_secondary_projectile.InitialVelocity = 60
|
sparrow_secondary_projectile.InitialVelocity = 60
|
||||||
sparrow_secondary_projectile.Lifespan = 5.85f
|
sparrow_secondary_projectile.Lifespan = 5.85f
|
||||||
|
sparrow_secondary_projectile.ExistsOnRemoteClients = true
|
||||||
|
sparrow_secondary_projectile.RemoteClientData = (13107, 187)
|
||||||
|
sparrow_secondary_projectile.AutoLock = true
|
||||||
|
sparrow_secondary_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile)
|
ProjectileDefinition.CalculateDerivedFields(sparrow_secondary_projectile)
|
||||||
|
|
||||||
spiker_projectile.Name = "spiker_projectile"
|
spiker_projectile.Name = "spiker_projectile"
|
||||||
|
|
@ -3447,6 +3434,10 @@ object GlobalDefinitions {
|
||||||
starfire_projectile.ProjectileDamageType = DamageType.Aggravated
|
starfire_projectile.ProjectileDamageType = DamageType.Aggravated
|
||||||
starfire_projectile.InitialVelocity = 45
|
starfire_projectile.InitialVelocity = 45
|
||||||
starfire_projectile.Lifespan = 7.8f
|
starfire_projectile.Lifespan = 7.8f
|
||||||
|
starfire_projectile.ExistsOnRemoteClients = true
|
||||||
|
starfire_projectile.RemoteClientData = (39577, 249)
|
||||||
|
starfire_projectile.AutoLock = true
|
||||||
|
starfire_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
|
ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
|
||||||
|
|
||||||
striker_missile_projectile.Name = "striker_missile_projectile"
|
striker_missile_projectile.Name = "striker_missile_projectile"
|
||||||
|
|
@ -3478,6 +3469,10 @@ object GlobalDefinitions {
|
||||||
striker_missile_targeting_projectile.ProjectileDamageType = DamageType.Splash
|
striker_missile_targeting_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
striker_missile_targeting_projectile.InitialVelocity = 30
|
striker_missile_targeting_projectile.InitialVelocity = 30
|
||||||
striker_missile_targeting_projectile.Lifespan = 4.2f
|
striker_missile_targeting_projectile.Lifespan = 4.2f
|
||||||
|
striker_missile_targeting_projectile.ExistsOnRemoteClients = true
|
||||||
|
striker_missile_targeting_projectile.RemoteClientData = (26214, 134)
|
||||||
|
striker_missile_targeting_projectile.AutoLock = true
|
||||||
|
striker_missile_targeting_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(striker_missile_targeting_projectile)
|
ProjectileDefinition.CalculateDerivedFields(striker_missile_targeting_projectile)
|
||||||
|
|
||||||
trek_projectile.Name = "trek_projectile"
|
trek_projectile.Name = "trek_projectile"
|
||||||
|
|
@ -3565,6 +3560,10 @@ object GlobalDefinitions {
|
||||||
wasp_rocket_projectile.ProjectileDamageType = DamageType.Splash
|
wasp_rocket_projectile.ProjectileDamageType = DamageType.Splash
|
||||||
wasp_rocket_projectile.InitialVelocity = 60
|
wasp_rocket_projectile.InitialVelocity = 60
|
||||||
wasp_rocket_projectile.Lifespan = 6.5f
|
wasp_rocket_projectile.Lifespan = 6.5f
|
||||||
|
wasp_rocket_projectile.ExistsOnRemoteClients = true
|
||||||
|
wasp_rocket_projectile.RemoteClientData = (0, 208)
|
||||||
|
wasp_rocket_projectile.AutoLock = true
|
||||||
|
wasp_rocket_projectile.Packet = projectileConverter
|
||||||
ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile)
|
ProjectileDefinition.CalculateDerivedFields(wasp_rocket_projectile)
|
||||||
|
|
||||||
winchester_projectile.Name = "winchester_projectile"
|
winchester_projectile.Name = "winchester_projectile"
|
||||||
|
|
@ -4337,6 +4336,7 @@ object GlobalDefinitions {
|
||||||
medicalapplicator.Name = "medicalapplicator"
|
medicalapplicator.Name = "medicalapplicator"
|
||||||
medicalapplicator.Size = EquipmentSize.Pistol
|
medicalapplicator.Size = EquipmentSize.Pistol
|
||||||
medicalapplicator.AmmoTypes += health_canister
|
medicalapplicator.AmmoTypes += health_canister
|
||||||
|
medicalapplicator.ProjectileTypes += no_projectile
|
||||||
medicalapplicator.FireModes += new FireModeDefinition
|
medicalapplicator.FireModes += new FireModeDefinition
|
||||||
medicalapplicator.FireModes.head.AmmoTypeIndices += 0
|
medicalapplicator.FireModes.head.AmmoTypeIndices += 0
|
||||||
medicalapplicator.FireModes.head.AmmoSlotIndex = 0
|
medicalapplicator.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
|
@ -4351,9 +4351,12 @@ object GlobalDefinitions {
|
||||||
nano_dispenser.Size = EquipmentSize.Rifle
|
nano_dispenser.Size = EquipmentSize.Rifle
|
||||||
nano_dispenser.AmmoTypes += armor_canister
|
nano_dispenser.AmmoTypes += armor_canister
|
||||||
nano_dispenser.AmmoTypes += upgrade_canister
|
nano_dispenser.AmmoTypes += upgrade_canister
|
||||||
|
nano_dispenser.ProjectileTypes += no_projectile
|
||||||
nano_dispenser.FireModes += new FireModeDefinition
|
nano_dispenser.FireModes += new FireModeDefinition
|
||||||
nano_dispenser.FireModes.head.AmmoTypeIndices += 0
|
nano_dispenser.FireModes.head.AmmoTypeIndices += 0
|
||||||
nano_dispenser.FireModes.head.AmmoTypeIndices += 1
|
nano_dispenser.FireModes.head.AmmoTypeIndices += 1
|
||||||
|
nano_dispenser.FireModes.head.ProjectileTypeIndices += 0 //armor_canister
|
||||||
|
nano_dispenser.FireModes.head.ProjectileTypeIndices += 0 //upgrade_canister
|
||||||
nano_dispenser.FireModes.head.AmmoSlotIndex = 0
|
nano_dispenser.FireModes.head.AmmoSlotIndex = 0
|
||||||
nano_dispenser.FireModes.head.Magazine = 100
|
nano_dispenser.FireModes.head.Magazine = 100
|
||||||
nano_dispenser.FireModes.head.CustomMagazine = Ammo.upgrade_canister -> 1
|
nano_dispenser.FireModes.head.CustomMagazine = Ammo.upgrade_canister -> 1
|
||||||
|
|
@ -4364,6 +4367,7 @@ object GlobalDefinitions {
|
||||||
bank.Name = "bank"
|
bank.Name = "bank"
|
||||||
bank.Size = EquipmentSize.Pistol
|
bank.Size = EquipmentSize.Pistol
|
||||||
bank.AmmoTypes += armor_canister
|
bank.AmmoTypes += armor_canister
|
||||||
|
bank.ProjectileTypes += no_projectile
|
||||||
bank.FireModes += new FireModeDefinition
|
bank.FireModes += new FireModeDefinition
|
||||||
bank.FireModes.head.AmmoTypeIndices += 0
|
bank.FireModes.head.AmmoTypeIndices += 0
|
||||||
bank.FireModes.head.AmmoSlotIndex = 0
|
bank.FireModes.head.AmmoSlotIndex = 0
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,16 @@ final case class Projectile(profile : ProjectileDefinition,
|
||||||
attribute_to : Int,
|
attribute_to : Int,
|
||||||
shot_origin : Vector3,
|
shot_origin : Vector3,
|
||||||
shot_angle : Vector3,
|
shot_angle : Vector3,
|
||||||
fire_time: Long = System.nanoTime) {
|
fire_time: Long = System.nanoTime) extends PlanetSideGameObject {
|
||||||
|
Position = shot_origin
|
||||||
|
Orientation = shot_angle
|
||||||
|
Velocity = {
|
||||||
|
val initVel : Int = profile.InitialVelocity //initial velocity
|
||||||
|
val radAngle : Double = math.toRadians(shot_angle.y) //angle of elevation
|
||||||
|
val rise : Float = initVel * math.sin(radAngle).toFloat //z
|
||||||
|
val ground : Float = initVel * math.cos(radAngle).toFloat //base
|
||||||
|
Vector3.Rz(Vector3(0, -ground, 0), shot_angle.z) + Vector3.z(rise)
|
||||||
|
}
|
||||||
/** Information about the current world coordinates and orientation of the projectile */
|
/** Information about the current world coordinates and orientation of the projectile */
|
||||||
val current : SimpleWorldEntity = new SimpleWorldEntity()
|
val current : SimpleWorldEntity = new SimpleWorldEntity()
|
||||||
private var resolved : ProjectileResolution.Value = ProjectileResolution.Unresolved
|
private var resolved : ProjectileResolution.Value = ProjectileResolution.Unresolved
|
||||||
|
|
@ -54,6 +63,8 @@ final case class Projectile(profile : ProjectileDefinition,
|
||||||
def isResolved : Boolean = resolved == ProjectileResolution.Resolved || resolved == ProjectileResolution.MissedShot
|
def isResolved : Boolean = resolved == ProjectileResolution.Resolved || resolved == ProjectileResolution.MissedShot
|
||||||
|
|
||||||
def isMiss : Boolean = resolved == ProjectileResolution.MissedShot
|
def isMiss : Boolean = resolved == ProjectileResolution.MissedShot
|
||||||
|
|
||||||
|
def Definition = profile
|
||||||
}
|
}
|
||||||
|
|
||||||
object Projectile {
|
object Projectile {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ package net.psforever.objects.ballistics
|
||||||
* An `Enumeration` of all the projectile types in the game, paired with their object id as the `Value`.
|
* An `Enumeration` of all the projectile types in the game, paired with their object id as the `Value`.
|
||||||
*/
|
*/
|
||||||
object Projectiles extends Enumeration {
|
object Projectiles extends Enumeration {
|
||||||
|
final val no_projectile = Value(0)
|
||||||
|
|
||||||
final val bullet_105mm_projectile = Value(1)
|
final val bullet_105mm_projectile = Value(1)
|
||||||
final val bullet_12mm_projectile = Value(4)
|
final val bullet_12mm_projectile = Value(4)
|
||||||
final val bullet_12mm_projectileb = Value(5)
|
final val bullet_12mm_projectileb = Value(5)
|
||||||
|
|
@ -92,6 +94,7 @@ object Projectiles extends Enumeration {
|
||||||
final val mine_projectile = Value(551)
|
final val mine_projectile = Value(551)
|
||||||
final val mine_sweeper_projectile = Value(554)
|
final val mine_sweeper_projectile = Value(554)
|
||||||
final val mine_sweeper_projectile_enh = Value(555)
|
final val mine_sweeper_projectile_enh = Value(555)
|
||||||
|
final val oicw_little_buddy = Value(601)
|
||||||
final val oicw_projectile = Value(602)
|
final val oicw_projectile = Value(602)
|
||||||
final val pellet_gun_projectile = Value(631)
|
final val pellet_gun_projectile = Value(631)
|
||||||
final val peregrine_dual_machine_gun_projectile = Value(639)
|
final val peregrine_dual_machine_gun_projectile = Value(639)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||||
private var damageAtEdge : Float = 1f
|
private var damageAtEdge : Float = 1f
|
||||||
private var damageRadius : Float = 1f
|
private var damageRadius : Float = 1f
|
||||||
private var useDamage1Subtract : Boolean = false
|
private var useDamage1Subtract : Boolean = false
|
||||||
|
private var existsOnRemoteClients : Boolean = false //`true` spawns a server-managed object
|
||||||
|
private var remoteClientData : (Int, Int) = (0, 0) //artificial values; for ObjectCreateMessage packet (oicw_little_buddy is undefined)
|
||||||
|
private var autoLock : Boolean = false
|
||||||
//derived calculations
|
//derived calculations
|
||||||
private var distanceMax : Float = 0f
|
private var distanceMax : Float = 0f
|
||||||
private var distanceFromAcceleration : Float = 0f
|
private var distanceFromAcceleration : Float = 0f
|
||||||
|
|
@ -109,6 +112,27 @@ class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId)
|
||||||
DamageRadius
|
DamageRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ExistsOnRemoteClients : Boolean = existsOnRemoteClients
|
||||||
|
|
||||||
|
def ExistsOnRemoteClients_=(existsOnRemoteClients : Boolean) : Boolean = {
|
||||||
|
this.existsOnRemoteClients = existsOnRemoteClients
|
||||||
|
ExistsOnRemoteClients
|
||||||
|
}
|
||||||
|
|
||||||
|
def RemoteClientData : (Int, Int) = remoteClientData
|
||||||
|
|
||||||
|
def RemoteClientData_=(remoteClientData : (Int, Int)) : (Int, Int) = {
|
||||||
|
this.remoteClientData = remoteClientData
|
||||||
|
RemoteClientData
|
||||||
|
}
|
||||||
|
|
||||||
|
def AutoLock : Boolean = autoLock
|
||||||
|
|
||||||
|
def AutoLock_=(lockState : Boolean) : Boolean = {
|
||||||
|
autoLock = lockState
|
||||||
|
AutoLock
|
||||||
|
}
|
||||||
|
|
||||||
def DistanceMax : Float = distanceMax //accessor only
|
def DistanceMax : Float = distanceMax //accessor only
|
||||||
|
|
||||||
def DistanceFromAcceleration : Float = distanceFromAcceleration //accessor only
|
def DistanceFromAcceleration : Float = distanceFromAcceleration //accessor only
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) 2019 PSForever
|
||||||
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
|
import net.psforever.objects.ballistics.Projectile
|
||||||
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
import net.psforever.packet.game.objectcreate.{CommonFieldData, CommonFieldDataWithPlacement, FlightPhysics, PlacementData, RemoteProjectileData}
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
class ProjectileConverter extends ObjectCreateConverter[Projectile]() {
|
||||||
|
override def ConstructorData(obj : Projectile) : Try[RemoteProjectileData] = {
|
||||||
|
Success(
|
||||||
|
RemoteProjectileData(
|
||||||
|
CommonFieldDataWithPlacement(
|
||||||
|
PlacementData(
|
||||||
|
obj.Position,
|
||||||
|
obj.Orientation,
|
||||||
|
obj.Velocity
|
||||||
|
),
|
||||||
|
CommonFieldData(
|
||||||
|
obj.owner.Faction,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
PlanetSideGUID(0)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
obj.profile.RemoteClientData._1,
|
||||||
|
obj.profile.RemoteClientData._2,
|
||||||
|
FlightPhysics.State4,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def DetailedConstructorData(obj : Projectile) : Try[RemoteProjectileData] =
|
||||||
|
Failure(new Exception("ProjectileConverter should not be used to generate detailed projectile data (nothing should)"))
|
||||||
|
}
|
||||||
|
|
@ -329,7 +329,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
case 0x08 => game.PlayerStateMessage.decode
|
case 0x08 => game.PlayerStateMessage.decode
|
||||||
case 0x09 => game.HitMessage.decode
|
case 0x09 => game.HitMessage.decode
|
||||||
case 0x0a => game.HitHint.decode
|
case 0x0a => game.HitHint.decode
|
||||||
case 0x0b => noDecoder(DamageMessage)
|
case 0x0b => game.DamageMessage.decode
|
||||||
case 0x0c => game.DestroyMessage.decode
|
case 0x0c => game.DestroyMessage.decode
|
||||||
case 0x0d => game.ReloadMessage.decode
|
case 0x0d => game.ReloadMessage.decode
|
||||||
case 0x0e => game.MountVehicleMsg.decode
|
case 0x0e => game.MountVehicleMsg.decode
|
||||||
|
|
@ -462,7 +462,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
case 0x78 => game.OxygenStateMessage.decode
|
case 0x78 => game.OxygenStateMessage.decode
|
||||||
case 0x79 => noDecoder(TradeMessage)
|
case 0x79 => noDecoder(TradeMessage)
|
||||||
case 0x7a => noDecoder(UnknownMessage122)
|
case 0x7a => noDecoder(UnknownMessage122)
|
||||||
case 0x7b => noDecoder(DamageFeedbackMessage)
|
case 0x7b => game.DamageFeedbackMessage.decode
|
||||||
case 0x7c => game.DismountBuildingMsg.decode
|
case 0x7c => game.DismountBuildingMsg.decode
|
||||||
case 0x7d => noDecoder(UnknownMessage125)
|
case 0x7d => noDecoder(UnknownMessage125)
|
||||||
case 0x7e => noDecoder(UnknownMessage126)
|
case 0x7e => noDecoder(UnknownMessage126)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright (c) 2019 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
|
final case class DamageFeedbackMessage(unk1 : Int,
|
||||||
|
unk2 : Boolean,
|
||||||
|
unk2a : Option[PlanetSideGUID],
|
||||||
|
unk2b : Option[String],
|
||||||
|
unk2c : Option[Int],
|
||||||
|
unk3 : Boolean,
|
||||||
|
unk3a : Option[PlanetSideGUID],
|
||||||
|
unk3b : Option[String],
|
||||||
|
unk3c : Option[Int],
|
||||||
|
unk3d : Option[Int],
|
||||||
|
unk4 : Int,
|
||||||
|
unk5 : Long,
|
||||||
|
unk6 : Int)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
assert(
|
||||||
|
{
|
||||||
|
val unk2aEmpty = unk2a.isEmpty
|
||||||
|
val unk2bEmpty = unk2b.isEmpty
|
||||||
|
val unk2cEmpty = unk2c.isEmpty
|
||||||
|
if(unk2a.nonEmpty) unk2bEmpty && unk2cEmpty
|
||||||
|
else if(unk2b.nonEmpty) unk2 && unk2aEmpty && unk2cEmpty
|
||||||
|
else unk2aEmpty && !unk2 && unk2bEmpty && unk2c.nonEmpty
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
{
|
||||||
|
val unk3aEmpty = unk3a.isEmpty
|
||||||
|
val unk3bEmpty = unk3b.isEmpty
|
||||||
|
val unk3cEmpty = unk3c.isEmpty
|
||||||
|
if(unk3a.nonEmpty) unk3bEmpty && unk3cEmpty
|
||||||
|
else if(unk3b.nonEmpty) unk3 && unk3aEmpty && unk3cEmpty
|
||||||
|
else unk3aEmpty && !unk3 && unk3bEmpty && unk3c.nonEmpty
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert(unk3a.isEmpty == unk3d.nonEmpty)
|
||||||
|
|
||||||
|
type Packet = DamageFeedbackMessage
|
||||||
|
def opcode = GamePacketOpcode.DamageFeedbackMessage
|
||||||
|
def encode = DamageFeedbackMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object DamageFeedbackMessage extends Marshallable[DamageFeedbackMessage] {
|
||||||
|
implicit val codec : Codec[DamageFeedbackMessage] = (
|
||||||
|
("unk1" | uint4) ::
|
||||||
|
(bool >>:~ { u2 =>
|
||||||
|
bool >>:~ { u3 =>
|
||||||
|
("unk2a" | conditional(u2, PlanetSideGUID.codec)) ::
|
||||||
|
(("unk2b" | conditional(!u2 && u3, PacketHelpers.encodedWideStringAligned(6))) >>:~ { u2b =>
|
||||||
|
("unk2c" | conditional(!(u2 && u3), uintL(11))) ::
|
||||||
|
(bool >>:~ { u5 =>
|
||||||
|
bool >>:~ { u6 =>
|
||||||
|
("unk3a" | conditional(u5, PlanetSideGUID.codec)) ::
|
||||||
|
("unk3b" | conditional(!u5 && u6, PacketHelpers.encodedWideStringAligned( if(u2b.nonEmpty) 3 else 1 ))) ::
|
||||||
|
("unk3c" | conditional(!(u5 && u6), uintL(11))) ::
|
||||||
|
("unk3d" | conditional(!u5, uint2)) ::
|
||||||
|
("unk4" | uint(3)) ::
|
||||||
|
("unk5" | uint32L) ::
|
||||||
|
("unk6" | uint2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).xmap[DamageFeedbackMessage] (
|
||||||
|
{
|
||||||
|
case u1 :: _ :: u2 :: u2a :: u2b :: u2c :: _ :: u3 :: u3a :: u3b :: u3c :: u3d :: u4 :: u5 :: u6 :: HNil =>
|
||||||
|
DamageFeedbackMessage(u1, u2, u2a, u2b, u2c, u3, u3a, u3b, u3c, u3d, u4, u5, u6)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case DamageFeedbackMessage(u1, u2, u2a, u2b, u2c, u3, u3a, u3b, u3c, u3d, u4, u5, u6) =>
|
||||||
|
u1 :: u2a.nonEmpty :: u2 :: u2a :: u2b :: u2c :: u3a.nonEmpty :: u3 :: u3a :: u3b :: u3c :: u3d :: u4 :: u5 :: u6 :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright (c) 2019 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
|
import net.psforever.types.Angular
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param guid1 na
|
||||||
|
* @param unk1 na
|
||||||
|
* @param guid2 na
|
||||||
|
* @param unk2 na
|
||||||
|
*/
|
||||||
|
final case class DamageMessage(guid1 : PlanetSideGUID,
|
||||||
|
unk1 : Int,
|
||||||
|
guid2 : PlanetSideGUID,
|
||||||
|
unk2 : Boolean)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = DamageMessage
|
||||||
|
def opcode = GamePacketOpcode.DamageMessage
|
||||||
|
def encode = DamageMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object DamageMessage extends Marshallable[DamageMessage] {
|
||||||
|
implicit val codec : Codec[DamageMessage] = (
|
||||||
|
("guid1" | PlanetSideGUID.codec) ::
|
||||||
|
("unk1" | uint8) ::
|
||||||
|
("guid1" | PlanetSideGUID.codec) ::
|
||||||
|
("unk2" | bool)
|
||||||
|
).as[DamageMessage]
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,8 @@ import shapeless.{::, HNil}
|
||||||
* @param player_guid1 the player
|
* @param player_guid1 the player
|
||||||
* @param player_guid2 the player(?);
|
* @param player_guid2 the player(?);
|
||||||
* often matches with `player_guid1`
|
* often matches with `player_guid1`
|
||||||
* @param unk na
|
* @param unk na;
|
||||||
|
* commonly, zero
|
||||||
* @param list list of detected objects;
|
* @param list list of detected objects;
|
||||||
* normally contains at least one element
|
* normally contains at least one element
|
||||||
*/
|
*/
|
||||||
|
|
@ -20,7 +21,7 @@ import shapeless.{::, HNil}
|
||||||
BETA CLIENT DEBUG INFO:
|
BETA CLIENT DEBUG INFO:
|
||||||
Detector
|
Detector
|
||||||
Sender
|
Sender
|
||||||
Object Count
|
Object Count (not really)
|
||||||
Detected Object[]
|
Detected Object[]
|
||||||
*/
|
*/
|
||||||
final case class ObjectDetectedMessage(player_guid1 : PlanetSideGUID,
|
final case class ObjectDetectedMessage(player_guid1 : PlanetSideGUID,
|
||||||
|
|
|
||||||
|
|
@ -2,49 +2,58 @@
|
||||||
package net.psforever.packet.game
|
package net.psforever.packet.game
|
||||||
|
|
||||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.{Angular, Vector3}
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatched to deliberately render certain projectiles of a weapon on other players' clients.<br>
|
* Dispatched to deliberately control certain projectiles of a weapon on other players' clients.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* This packet is generated by firing specific weapons in specific fire modes.
|
* This packet should be generated by firing specific weapons in specific fire modes.
|
||||||
* For example, the Phoenix (`hunterseeker`) discharged in its primary fire mode generates this packet;
|
* For example, the Phoenix (`hunterseeker`) discharged in its primary fire mode generates this packet;
|
||||||
* but, the Phoenix in secondary fire mode does not.
|
* but, the Phoenix in secondary fire mode does not.
|
||||||
* The Striker (`striker`) discharged in its primary fire mode generates this packet;
|
* The Striker (`striker`) discharged in its primary fire mode generates this packet;
|
||||||
* but, the Striker in secondary fire mode does not.
|
* but, the Striker in secondary fire mode does not.
|
||||||
* The chosen fire mode(s) are not a straight-fire projectile but one that has special control asserted over it.
|
* The chosen fire mode(s) are not a straight-fire projectile but one that has special control asserted over it.
|
||||||
* For the Phoenix, it is user-operated.
|
* For the Phoenix, it is user operated (camera-guided).
|
||||||
* For the Striker, it tracks towards a target while the weapon's reticle hovers over that target.<br>
|
* For the Striker, it tracks towards a valid target while the weapon's reticle hovers over that target.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* This packet will continue to be dispatched by the client for as long as the projectile being tracked is in the air.
|
* This packet will continue to be dispatched by the client for as long as the projectile being tracked is in the air.
|
||||||
* All projectiles have a maximum lifespan before they will lose control and either despawn and/or explode.
|
* All projectiles have a maximum lifespan before they will lose control and either despawn and/or explode.
|
||||||
* This number is tracked in the packet for simplicity.
|
* This number is tracked in the packet for simplicity.
|
||||||
* If the projectile strikes a valid target, the count will jump to a significantly enormous value beyond its normal lifespan.
|
* <br>
|
||||||
* This ensures that the projectile - locally and the shared model - will despawn.
|
* This control can not be demonstrated until that projectile is physically constructed on the other clients
|
||||||
* @param projectile_guid the projectile
|
* in the same way that a player or a vehicle is constructed.
|
||||||
|
* A projectile that exhibits intentional construction behavior is flagged using the property `exists_on_remote_client`.
|
||||||
|
* The model comes with a number of caveats,
|
||||||
|
* some that originate from the object construction process itself,
|
||||||
|
* but also some from this packet.
|
||||||
|
* For example,
|
||||||
|
* as indicated by the static `shot_original_orient` values reported by this packet.
|
||||||
|
* a discharged controlled projectile will not normally rotate.
|
||||||
|
* A minor loss of lifespan may be levied.
|
||||||
|
* @see `ProjectileDefinition`
|
||||||
|
* @see `RemoteProjectileData`
|
||||||
|
* @param projectile_guid when dispatched by the client, the client-specific local unique identifier of the projectile;
|
||||||
|
* when dispatched by the server, the global unique identifier for the synchronized projectile object
|
||||||
* @param shot_pos the position of the projectile
|
* @param shot_pos the position of the projectile
|
||||||
* @param shot_vel the velocity of the projectile
|
* @param shot_vel the velocity of the projectile
|
||||||
* @param unk1 na;
|
* @param shot_original_orient the orientation of the projectile when it was discharged
|
||||||
* usually 0
|
* @param sequence_num an incrementing index of the packet in this projectile's lifetime;
|
||||||
* @param unk2 na;
|
* suggests the "time alive" and indicates a place in packet ordering
|
||||||
* will remain consistent for the lifespan of a given projectile in most cases
|
* @param end indicates the projectile has reached the end of its lifespan;
|
||||||
* @param unk3 na;
|
* usually, it should explode
|
||||||
* will remain consistent for the lifespan of a given projectile in most cases
|
* @param hit_target_guid the global unique identifier of the object the projwectile collided with;
|
||||||
* @param unk4 na;
|
* will be 0 if it reached the end of its life naturally, without colliding with anything
|
||||||
* usually false
|
|
||||||
* @param time_alive how long the projectile has been in the air;
|
|
||||||
* often expressed in multiples of 2
|
|
||||||
*/
|
*/
|
||||||
final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID,
|
final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID,
|
||||||
shot_pos : Vector3,
|
shot_pos : Vector3,
|
||||||
shot_vel : Vector3,
|
shot_vel : Vector3,
|
||||||
unk1 : Int,
|
shot_original_orient : Vector3,
|
||||||
unk2 : Int,
|
sequence_num : Int,
|
||||||
unk3 : Int,
|
end : Boolean,
|
||||||
unk4 : Boolean,
|
hit_target_guid : PlanetSideGUID)
|
||||||
time_alive : Int)
|
|
||||||
extends PlanetSideGamePacket {
|
extends PlanetSideGamePacket {
|
||||||
type Packet = ProjectileStateMessage
|
type Packet = ProjectileStateMessage
|
||||||
def opcode = GamePacketOpcode.ProjectileStateMessage
|
def opcode = GamePacketOpcode.ProjectileStateMessage
|
||||||
|
|
@ -56,10 +65,20 @@ object ProjectileStateMessage extends Marshallable[ProjectileStateMessage] {
|
||||||
("projectile_guid" | PlanetSideGUID.codec) ::
|
("projectile_guid" | PlanetSideGUID.codec) ::
|
||||||
("shot_pos" | Vector3.codec_pos) ::
|
("shot_pos" | Vector3.codec_pos) ::
|
||||||
("shot_vel" | Vector3.codec_float) ::
|
("shot_vel" | Vector3.codec_float) ::
|
||||||
("unk1" | uint8L) ::
|
("roll" | Angular.codec_roll) ::
|
||||||
("unk2" | uint8L) ::
|
("pitch" | Angular.codec_pitch) ::
|
||||||
("unk3" | uint8L) ::
|
("yaw" | Angular.codec_yaw()) ::
|
||||||
("unk4" | bool) ::
|
("sequence_num" | uint8) ::
|
||||||
("time_alive" | uint16L)
|
("end" | bool) ::
|
||||||
).as[ProjectileStateMessage]
|
("hit_target" | PlanetSideGUID.codec)
|
||||||
|
).xmap[ProjectileStateMessage] (
|
||||||
|
{
|
||||||
|
case guid :: pos :: vel :: roll :: pitch :: yaw :: sequence_num :: explode :: unk :: HNil =>
|
||||||
|
ProjectileStateMessage(guid, pos, vel, Vector3(roll, pitch, yaw), sequence_num , explode, unk)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case ProjectileStateMessage(guid, pos, vel, Vector3(roll, pitch, yaw), sequence_num, explode, unk) =>
|
||||||
|
guid :: pos :: vel :: roll :: pitch :: yaw :: sequence_num :: explode :: unk :: HNil
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,7 @@ object ObjectClass {
|
||||||
final val portable_manned_turret_tr = 687
|
final val portable_manned_turret_tr = 687
|
||||||
final val portable_manned_turret_vs = 688
|
final val portable_manned_turret_vs = 688
|
||||||
//projectiles
|
//projectiles
|
||||||
final val hunter_seeker_missile_projectile = 405
|
final val hunter_seeker_missile_projectile = 405 //phoenix projectile
|
||||||
final val meteor_common = 543
|
final val meteor_common = 543
|
||||||
final val meteor_projectile_b_large = 544
|
final val meteor_projectile_b_large = 544
|
||||||
final val meteor_projectile_b_medium = 545
|
final val meteor_projectile_b_medium = 545
|
||||||
|
|
@ -303,13 +303,14 @@ object ObjectClass {
|
||||||
final val meteor_projectile_large = 547
|
final val meteor_projectile_large = 547
|
||||||
final val meteor_projectile_medium = 548
|
final val meteor_projectile_medium = 548
|
||||||
final val meteor_projectile_small = 549
|
final val meteor_projectile_small = 549
|
||||||
final val oicw_little_buddy = 601
|
final val phoenix_missile_guided_projectile = 675 //decimator projectile
|
||||||
final val oicw_projectile = 602
|
final val oicw_little_buddy = 601 //scorpion projectile's projectiles
|
||||||
|
final val oicw_projectile = 602 //scorpion projectile
|
||||||
final val radiator_cload = 717
|
final val radiator_cload = 717
|
||||||
final val sparrow_projectile = 792
|
final val sparrow_projectile = 792 //nc aa max projectile
|
||||||
final val starfire_projectile = 831
|
final val starfire_projectile = 831 //vs aa max projectile
|
||||||
final val striker_missile_targeting_projectile = 841
|
final val striker_missile_targeting_projectile = 841 //striker projectile
|
||||||
final val wasp_rocket_projectile = 1001
|
final val wasp_rocket_projectile = 1001 //wasp projectile
|
||||||
//vehicles
|
//vehicles
|
||||||
final val apc_destroyed = 65
|
final val apc_destroyed = 65
|
||||||
final val apc_tr = 67 //juggernaut
|
final val apc_tr = 67 //juggernaut
|
||||||
|
|
@ -1222,19 +1223,21 @@ object ObjectClass {
|
||||||
case ObjectClass.portable_manned_turret_vs => ConstructorData(OneMannedFieldTurretData.codec, "field turret")
|
case ObjectClass.portable_manned_turret_vs => ConstructorData(OneMannedFieldTurretData.codec, "field turret")
|
||||||
case ObjectClass.router_telepad_deployable => DroppedItemData(TelepadDeployableData.codec, "telepad deployable")
|
case ObjectClass.router_telepad_deployable => DroppedItemData(TelepadDeployableData.codec, "telepad deployable")
|
||||||
//projectiles
|
//projectiles
|
||||||
case ObjectClass.hunter_seeker_missile_projectile => ConstructorData(TrackedProjectileData.codec, "projectile")
|
case ObjectClass.hunter_seeker_missile_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
case ObjectClass.meteor_common => ConstructorData(TrackedProjectileData.codec, "meteor")
|
case ObjectClass.meteor_common => ConstructorData(RemoteProjectileData.codec, "meteor")
|
||||||
case ObjectClass.meteor_projectile_b_large => ConstructorData(TrackedProjectileData.codec, "meteor")
|
case ObjectClass.meteor_projectile_b_large => ConstructorData(RemoteProjectileData.codec, "meteor")
|
||||||
case ObjectClass.meteor_projectile_b_medium => ConstructorData(TrackedProjectileData.codec, "meteor")
|
case ObjectClass.meteor_projectile_b_medium => ConstructorData(RemoteProjectileData.codec, "meteor")
|
||||||
case ObjectClass.meteor_projectile_b_small => ConstructorData(TrackedProjectileData.codec, "meteor")
|
case ObjectClass.meteor_projectile_b_small => ConstructorData(RemoteProjectileData.codec, "meteor")
|
||||||
case ObjectClass.meteor_projectile_large => ConstructorData(TrackedProjectileData.codec, "meteor")
|
case ObjectClass.meteor_projectile_large => ConstructorData(RemoteProjectileData.codec, "meteor")
|
||||||
case ObjectClass.meteor_projectile_medium => ConstructorData(TrackedProjectileData.codec, "meteor")
|
case ObjectClass.meteor_projectile_medium => ConstructorData(RemoteProjectileData.codec, "meteor")
|
||||||
case ObjectClass.meteor_projectile_small => ConstructorData(TrackedProjectileData.codec, "meteor")
|
case ObjectClass.meteor_projectile_small => ConstructorData(RemoteProjectileData.codec, "meteor")
|
||||||
case ObjectClass.oicw_projectile => ConstructorData(TrackedProjectileData.codec, "projectile")
|
case ObjectClass.phoenix_missile_guided_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
case ObjectClass.sparrow_projectile => ConstructorData(TrackedProjectileData.codec, "projectile")
|
case ObjectClass.oicw_little_buddy => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
case ObjectClass.starfire_projectile => ConstructorData(TrackedProjectileData.codec, "projectile")
|
case ObjectClass.oicw_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
case ObjectClass.striker_missile_targeting_projectile => ConstructorData(TrackedProjectileData.codec, "projectile")
|
case ObjectClass.sparrow_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
case ObjectClass.wasp_rocket_projectile => ConstructorData(TrackedProjectileData.codec, "projectile")
|
case ObjectClass.starfire_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
|
case ObjectClass.striker_missile_targeting_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
|
case ObjectClass.wasp_rocket_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
|
||||||
//vehicles
|
//vehicles
|
||||||
case ObjectClass.ams => ConstructorData(VehicleData.codec(VehicleFormat.Utility), "ams")
|
case ObjectClass.ams => ConstructorData(VehicleData.codec(VehicleFormat.Utility), "ams")
|
||||||
case ObjectClass.ams_destroyed => ConstructorData(DestroyedVehicleData.codec, "wreckage")
|
case ObjectClass.ams_destroyed => ConstructorData(DestroyedVehicleData.codec, "wreckage")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||||
|
import scodec.{Attempt, Codec}
|
||||||
|
import scodec.codecs._
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
|
object RemoteProjectiles {
|
||||||
|
abstract class Data(val a : Int, val b : Int)
|
||||||
|
|
||||||
|
final case object Meteor extends Data(0, 32)
|
||||||
|
final case object Wasp extends Data(0, 208)
|
||||||
|
final case object Sparrow extends Data(13107, 187)
|
||||||
|
final case object OICW extends Data(13107, 195)
|
||||||
|
final case object Striker extends Data(26214, 134)
|
||||||
|
final case object HunterSeeker extends Data(39577, 201)
|
||||||
|
final case object Starfire extends Data(39577, 249)
|
||||||
|
class OICWLittleBuddy(x : Int, y : Int) extends Data(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
object FlightPhysics extends Enumeration {
|
||||||
|
type Type = Value
|
||||||
|
|
||||||
|
//valid (extremely small distance) (requires non-zero unk4, unk5)
|
||||||
|
val State3 = Value(3)
|
||||||
|
//valid (infinite) (if unk4 == 0 unk5 == 0, minimum distance + time)
|
||||||
|
val State4 = Value(4)
|
||||||
|
//valid(infinite)
|
||||||
|
val State5 = Value(5)
|
||||||
|
//valid (uses velocity) (infinite)
|
||||||
|
val State6 = Value(6)
|
||||||
|
//valid (uses velocity) (infinite)
|
||||||
|
val State7 = Value(7)
|
||||||
|
//valid (uses velocity) (time > 0 is infinite) (unk5 == 2)
|
||||||
|
val State15 = Value(15)
|
||||||
|
|
||||||
|
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a projectile that the server must intentionally convey to players other than the shooter.
|
||||||
|
* @param common_data common game object information
|
||||||
|
* @param u1 na;
|
||||||
|
* first part of the canned remote projectile data
|
||||||
|
* @param u2 na;
|
||||||
|
* second part of the canned remote projectile data
|
||||||
|
* @param unk3 na;
|
||||||
|
* does something to how the projectile flies
|
||||||
|
* @param unk4 na
|
||||||
|
* @param unk5 na
|
||||||
|
*/
|
||||||
|
final case class RemoteProjectileData(common_data : CommonFieldDataWithPlacement,
|
||||||
|
u1 : Int,
|
||||||
|
u2 : Int,
|
||||||
|
unk3 : FlightPhysics.Value,
|
||||||
|
unk4 : Int,
|
||||||
|
unk5 : Int
|
||||||
|
) extends ConstructorData {
|
||||||
|
override def bitsize : Long = 33L + common_data.bitsize
|
||||||
|
}
|
||||||
|
|
||||||
|
object RemoteProjectileData extends Marshallable[RemoteProjectileData] {
|
||||||
|
implicit val codec : Codec[RemoteProjectileData] = (
|
||||||
|
("data" | CommonFieldDataWithPlacement.codec) ::
|
||||||
|
("u1" | uint16) ::
|
||||||
|
("u2" | uint8) ::
|
||||||
|
("unk3" | FlightPhysics.codec) ::
|
||||||
|
("unk4" | uint(3)) ::
|
||||||
|
("unk5" | uint2)
|
||||||
|
).exmap[RemoteProjectileData] (
|
||||||
|
{
|
||||||
|
case data :: u1 :: u2 :: unk3 :: unk4 :: unk5 :: HNil =>
|
||||||
|
Attempt.successful(RemoteProjectileData(data, u1, u2, unk3, unk4, unk5))
|
||||||
|
|
||||||
|
// case data =>
|
||||||
|
// Attempt.failure(Err(s"invalid projectile data format - $data"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case RemoteProjectileData(data, u1, u2, unk3, unk4, unk5) =>
|
||||||
|
Attempt.successful(data :: u1 :: u2 :: unk3 :: unk4 :: unk5 :: HNil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package net.psforever.packet.game.objectcreate
|
|
||||||
|
|
||||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
|
||||||
import scodec.{Attempt, Codec, Err}
|
|
||||||
import scodec.codecs._
|
|
||||||
import shapeless.{::, HNil}
|
|
||||||
|
|
||||||
object TrackedProjectile extends Enumeration {
|
|
||||||
type Type = Value
|
|
||||||
|
|
||||||
val Meteor = Value(32)
|
|
||||||
val WaspRocket = Value(208)
|
|
||||||
val Sparrow = Value(3355579)
|
|
||||||
val OICW = Value(3355587)
|
|
||||||
val Striker = Value(6710918)
|
|
||||||
val HunterSeeker = Value(10131913)
|
|
||||||
val Starfire = Value(10131961)
|
|
||||||
|
|
||||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint24)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A representation of a projectile that the server must intentionally convey to players other than the shooter.
|
|
||||||
* @param data na
|
|
||||||
* @param unk2 na;
|
|
||||||
* data specific to the type of projectile(?)
|
|
||||||
* @param unk3 na
|
|
||||||
*/
|
|
||||||
final case class TrackedProjectileData(data : CommonFieldDataWithPlacement,
|
|
||||||
unk2 : TrackedProjectile.Value,
|
|
||||||
unk3 : Int = 0
|
|
||||||
) extends ConstructorData {
|
|
||||||
override def bitsize : Long = 33L + data.bitsize
|
|
||||||
}
|
|
||||||
|
|
||||||
object TrackedProjectileData extends Marshallable[TrackedProjectileData] {
|
|
||||||
implicit val codec : Codec[TrackedProjectileData] = (
|
|
||||||
("data" | CommonFieldDataWithPlacement.codec) ::
|
|
||||||
("unk2" | TrackedProjectile.codec) ::
|
|
||||||
uint4 ::
|
|
||||||
uint(3) ::
|
|
||||||
uint2
|
|
||||||
).exmap[TrackedProjectileData] (
|
|
||||||
{
|
|
||||||
case data :: unk2 :: 4 :: unk3 :: 0 :: HNil =>
|
|
||||||
Attempt.successful(TrackedProjectileData(data, unk2, unk3))
|
|
||||||
|
|
||||||
case data =>
|
|
||||||
Attempt.failure(Err(s"invalid projectile data format - $data"))
|
|
||||||
},
|
|
||||||
{
|
|
||||||
case TrackedProjectileData(data, unk2, unk3) =>
|
|
||||||
Attempt.successful(data :: unk2 :: 4 :: unk3 :: 0 :: HNil)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package services.avatar
|
package services.avatar
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Props}
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
import net.psforever.packet.game.ObjectCreateMessage
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
|
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
|
||||||
import services.avatar.support.{CorpseRemovalActor, DroppedItemRemover}
|
import services.avatar.support.{CorpseRemovalActor, DroppedItemRemover}
|
||||||
import services.{GenericEventBus, RemoverActor, Service}
|
import services.{GenericEventBus, RemoverActor, Service}
|
||||||
|
|
@ -109,6 +109,10 @@ class AvatarService extends Actor {
|
||||||
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData))
|
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
case AvatarAction.GenericObjectAction(player_guid, object_guid, action_code) =>
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.GenericObjectAction(object_guid, action_code))
|
||||||
|
)
|
||||||
case AvatarAction.HitHint(source_guid, player_guid) =>
|
case AvatarAction.HitHint(source_guid, player_guid) =>
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.HitHint(source_guid))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.HitHint(source_guid))
|
||||||
|
|
@ -127,6 +131,12 @@ class AvatarService extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pkt))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pkt))
|
||||||
)
|
)
|
||||||
|
case AvatarAction.LoadProjectile(player_guid, object_id, object_guid, cdata) =>
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadProjectile(
|
||||||
|
ObjectCreateMessage(object_id, object_guid, cdata)
|
||||||
|
))
|
||||||
|
)
|
||||||
case AvatarAction.ObjectDelete(player_guid, item_guid, unk) =>
|
case AvatarAction.ObjectDelete(player_guid, item_guid, unk) =>
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectDelete(item_guid, unk))
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectDelete(item_guid, unk))
|
||||||
|
|
@ -151,6 +161,18 @@ class AvatarService extends Actor {
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(pos, vel, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectating, weaponInHand))
|
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(pos, vel, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectating, weaponInHand))
|
||||||
)
|
)
|
||||||
|
case AvatarAction.ProjectileAutoLockAwareness(mode) =>
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", PlanetSideGUID(0), AvatarResponse.ProjectileAutoLockAwareness(mode))
|
||||||
|
)
|
||||||
|
case AvatarAction.ProjectileExplodes(player_guid, projectile_guid, projectile) =>
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ProjectileExplodes(projectile_guid, projectile))
|
||||||
|
)
|
||||||
|
case AvatarAction.ProjectileState(player_guid, projectile_guid, shot_pos, shot_vel, shot_orient, sequence, end, target) =>
|
||||||
|
AvatarEvents.publish(
|
||||||
|
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, sequence, end, target))
|
||||||
|
)
|
||||||
case AvatarAction.PickupItem(player_guid, zone, target, slot, item, unk) =>
|
case AvatarAction.PickupItem(player_guid, zone, target, slot, item, unk) =>
|
||||||
janitor forward RemoverActor.ClearSpecific(List(item), zone)
|
janitor forward RemoverActor.ClearSpecific(List(item), zone)
|
||||||
AvatarEvents.publish(
|
AvatarEvents.publish(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package services.avatar
|
package services.avatar
|
||||||
|
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||||
import net.psforever.objects.ce.Deployable
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.inventory.Container
|
import net.psforever.objects.inventory.Container
|
||||||
|
|
@ -30,16 +30,18 @@ object AvatarAction {
|
||||||
final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||||
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
||||||
final case class EnvironmentalDamage(player_guid : PlanetSideGUID, amont: Int) extends Action
|
final case class EnvironmentalDamage(player_guid : PlanetSideGUID, amount: Int) extends Action
|
||||||
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : Any=>Unit) extends Action
|
final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : Any=>Unit) extends Action
|
||||||
final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action
|
final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action
|
||||||
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
|
final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action
|
||||||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action
|
||||||
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
||||||
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||||
|
final case class GenericObjectAction(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, action_code : Int) extends Action
|
||||||
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
|
final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action
|
||||||
final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action
|
final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action
|
||||||
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
|
final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action
|
||||||
|
final case class LoadProjectile(player_guid : PlanetSideGUID, object_id : Int, projectile_guid : PlanetSideGUID, cdata : ConstructorData) extends Action
|
||||||
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
||||||
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
||||||
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||||
|
|
@ -47,6 +49,9 @@ object AvatarAction {
|
||||||
final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||||
final case class PlayerState(player_guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Action
|
final case class PlayerState(player_guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Action
|
||||||
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
|
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
|
||||||
|
final case class ProjectileAutoLockAwareness(mode : Int) extends Action
|
||||||
|
final case class ProjectileExplodes(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, projectile : Projectile) extends Action
|
||||||
|
final case class ProjectileState(player_guid : PlanetSideGUID, projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, sequence : Int, end : Boolean, hit_target : PlanetSideGUID) extends Action
|
||||||
final case class PutDownFDU(player_guid : PlanetSideGUID) extends Action
|
final case class PutDownFDU(player_guid : PlanetSideGUID) extends Action
|
||||||
final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action
|
final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action
|
||||||
final case class Revive(target_guid: PlanetSideGUID) extends Action
|
final case class Revive(target_guid: PlanetSideGUID) extends Action
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package services.avatar
|
package services.avatar
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.ballistics.SourceEntry
|
import net.psforever.objects.ballistics.{Projectile, SourceEntry}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||||
|
|
@ -30,15 +30,20 @@ object AvatarResponse {
|
||||||
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response
|
final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response
|
||||||
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
||||||
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
|
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
|
||||||
|
final case class GenericObjectAction(object_guid : PlanetSideGUID, action_code : Int) extends Response
|
||||||
final case class HitHint(source_guid : PlanetSideGUID) extends Response
|
final case class HitHint(source_guid : PlanetSideGUID) extends Response
|
||||||
final case class KilledWhileInVehicle() extends Response
|
final case class KilledWhileInVehicle() extends Response
|
||||||
final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response
|
final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response
|
||||||
|
final case class LoadProjectile(pkt : ObjectCreateMessage) extends Response
|
||||||
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
||||||
final case class ObjectHeld(slot : Int) extends Response
|
final case class ObjectHeld(slot : Int) extends Response
|
||||||
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
||||||
final case class PlanetsideAttributeToAll(attribute_type : Int, attribute_value : Long) extends Response
|
final case class PlanetsideAttributeToAll(attribute_type : Int, attribute_value : Long) extends Response
|
||||||
final case class PlanetsideAttributeSelf(attribute_type : Int, attribute_value : Long) extends Response
|
final case class PlanetsideAttributeSelf(attribute_type : Int, attribute_value : Long) extends Response
|
||||||
final case class PlayerState(pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Response
|
final case class PlayerState(pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Response
|
||||||
|
final case class ProjectileAutoLockAwareness(mode : Int) extends Response
|
||||||
|
final case class ProjectileExplodes(projectile_guid : PlanetSideGUID, projectile : Projectile) extends Response
|
||||||
|
final case class ProjectileState(projectile_guid : PlanetSideGUID, shot_pos : Vector3, shot_vel : Vector3, shot_orient : Vector3, sequence : Int, end : Boolean, hit_target : PlanetSideGUID) extends Response
|
||||||
final case class PutDownFDU(target_guid : PlanetSideGUID) extends Response
|
final case class PutDownFDU(target_guid : PlanetSideGUID) extends Response
|
||||||
final case class Release(player : Player) extends Response
|
final case class Release(player : Player) extends Response
|
||||||
final case class Reload(weapon_guid : PlanetSideGUID) extends Response
|
final case class Reload(weapon_guid : PlanetSideGUID) extends Response
|
||||||
|
|
|
||||||
68
common/src/test/scala/game/DamageFeedbackMessageTest.scala
Normal file
68
common/src/test/scala/game/DamageFeedbackMessageTest.scala
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class DamageFeedbackMessageTest extends Specification {
|
||||||
|
val string = hex"7b 3d842f610b2040000000"
|
||||||
|
|
||||||
|
"decode" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case DamageFeedbackMessage(unk1, unk2, unk2a, unk2b, unk2c, unk3, unk3a, unk3b, unk3c, unk3d, unk4, unk5, unk6) =>
|
||||||
|
unk1 mustEqual 3
|
||||||
|
unk2 mustEqual true
|
||||||
|
unk2a.contains(PlanetSideGUID(2913)) mustEqual true
|
||||||
|
unk2b.isEmpty mustEqual true
|
||||||
|
unk2c.isEmpty mustEqual true
|
||||||
|
unk3 mustEqual true
|
||||||
|
unk3a.contains(PlanetSideGUID(2913)) mustEqual true
|
||||||
|
unk3b.isEmpty mustEqual true
|
||||||
|
unk3c.isEmpty mustEqual true
|
||||||
|
unk3d.isEmpty mustEqual true
|
||||||
|
unk4 mustEqual 1
|
||||||
|
unk5 mustEqual 2
|
||||||
|
unk6 mustEqual 0
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val msg = DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
|
||||||
|
"assert catches" in {
|
||||||
|
//unk2: no parameters
|
||||||
|
DamageFeedbackMessage(3, true, None, None, None, true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
//unk2: two exclusive parameters
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), Some("error"), None, true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, Some(5), true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
DamageFeedbackMessage(3, true, None, Some("error"), Some(5), true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
//unk2: all parameters
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), Some("error"), Some(5), true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
//unk2: mismatched flag for strings
|
||||||
|
DamageFeedbackMessage(3, true, None, None, Some(5), true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
DamageFeedbackMessage(3, false, None, Some("error"), None, true, Some(PlanetSideGUID(2913)), None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
|
||||||
|
//unk3: no parameters
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, None, None, None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
//unk3: two exclusive parameters
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, Some(PlanetSideGUID(2913)), Some("error"), None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, Some(PlanetSideGUID(2913)), None, Some(5), None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, None, Some("error"), Some(5), Some(1), 1, 2, 0) must throwA[AssertionError]
|
||||||
|
//unk3: all parameters
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, Some(PlanetSideGUID(2913)), Some("error"), Some(5), None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
//unk3: mismatched fields
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, Some(PlanetSideGUID(2913)), None, None, Some(5), 1, 2, 0) must throwA[AssertionError]
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, None, Some("Error"), None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
//unk3: mismatched flag for strings
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, true, None, None, Some(5), None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
DamageFeedbackMessage(3, true, Some(PlanetSideGUID(2913)), None, None, false, None, Some("error"), None, None, 1, 2, 0) must throwA[AssertionError]
|
||||||
|
}
|
||||||
|
}
|
||||||
30
common/src/test/scala/game/DamageMessageTest.scala
Normal file
30
common/src/test/scala/game/DamageMessageTest.scala
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2019 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class DamageMessageTest extends Specification {
|
||||||
|
val string = hex"0b610b02610b00"
|
||||||
|
|
||||||
|
"decode" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case DamageMessage(guid1, unk1, guid2, unk2) =>
|
||||||
|
guid1 mustEqual PlanetSideGUID(2913)
|
||||||
|
unk1 mustEqual 2
|
||||||
|
guid2 mustEqual PlanetSideGUID(2913)
|
||||||
|
unk2 mustEqual false
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val msg = DamageMessage(PlanetSideGUID(2913), 2, PlanetSideGUID(2913), false)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,23 +8,18 @@ import net.psforever.types.Vector3
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
class ProjectileStateMessageTest extends Specification {
|
class ProjectileStateMessageTest extends Specification {
|
||||||
val string = hex"3f 259d c5019 30e4a 9514 c52c9541 d9ba05c2 c5973941 00 f8 ec 020000"
|
val string = hex"3f 259d c5019 30e4a 9514 c52c9541 d9ba05c2 c5973941 00 f8 ec 02000000"
|
||||||
|
|
||||||
"decode" in {
|
"decode" in {
|
||||||
PacketCoding.DecodePacket(string).require match {
|
PacketCoding.DecodePacket(string).require match {
|
||||||
case ProjectileStateMessage(projectile, pos, vel, unk1, unk2, unk3, unk4, time_alive) =>
|
case ProjectileStateMessage(projectile, pos, vel, orient, sequence, explode, unk) =>
|
||||||
projectile mustEqual PlanetSideGUID(40229)
|
projectile mustEqual PlanetSideGUID(40229)
|
||||||
pos.x mustEqual 4611.539f
|
pos mustEqual Vector3(4611.539f, 5576.375f, 82.328125f)
|
||||||
pos.y mustEqual 5576.375f
|
vel mustEqual Vector3(18.64686f, -33.43247f, 11.599553f)
|
||||||
pos.z mustEqual 82.328125f
|
orient mustEqual Vector3(0, 22.5f, 146.25f)
|
||||||
vel.x mustEqual 18.64686f
|
sequence mustEqual 2
|
||||||
vel.y mustEqual -33.43247f
|
explode mustEqual false
|
||||||
vel.z mustEqual 11.599553f
|
unk mustEqual PlanetSideGUID(0)
|
||||||
unk1 mustEqual 0
|
|
||||||
unk2 mustEqual 248
|
|
||||||
unk3 mustEqual 236
|
|
||||||
unk4 mustEqual false
|
|
||||||
time_alive mustEqual 4
|
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
@ -35,10 +30,18 @@ class ProjectileStateMessageTest extends Specification {
|
||||||
PlanetSideGUID(40229),
|
PlanetSideGUID(40229),
|
||||||
Vector3(4611.539f, 5576.375f, 82.328125f),
|
Vector3(4611.539f, 5576.375f, 82.328125f),
|
||||||
Vector3(18.64686f, -33.43247f, 11.599553f),
|
Vector3(18.64686f, -33.43247f, 11.599553f),
|
||||||
0, 248, 236, false, 4
|
Vector3(0, 22.5f, 146.25f),
|
||||||
|
2,
|
||||||
|
false,
|
||||||
|
PlanetSideGUID(0)
|
||||||
)
|
)
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string
|
//pkt mustEqual string
|
||||||
|
val pkt_bits = pkt.toBitVector
|
||||||
|
val str_bits = string.toBitVector
|
||||||
|
pkt_bits.take(184) mustEqual str_bits.take(184) //skip 1 bit
|
||||||
|
pkt_bits.drop(185).take(7) mustEqual str_bits.drop(185).take(7) //skip 1 bit
|
||||||
|
pkt_bits.drop(193) mustEqual str_bits.drop(193)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.packet.PacketCoding
|
||||||
|
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class RemoteProjectileDataTest extends Specification {
|
||||||
|
val string_striker_projectile = hex"17 C5000000 A4B 009D 4C129 0CB0A 9814 00 F5 E3 040000666686400"
|
||||||
|
val string_hunter_seeker_missile_projectile = hex"17 c5000000 ca9 ab9e af127 ec465 3723 00 15 c4 2400009a99c9400"
|
||||||
|
|
||||||
|
"RemoteProjectileData" should {
|
||||||
|
"decode (striker_missile_targeting_projectile)" in {
|
||||||
|
PacketCoding.DecodePacket(string_striker_projectile).require match {
|
||||||
|
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||||
|
len mustEqual 197
|
||||||
|
cls mustEqual ObjectClass.striker_missile_targeting_projectile
|
||||||
|
guid mustEqual PlanetSideGUID(40192)
|
||||||
|
parent.isDefined mustEqual false
|
||||||
|
data match {
|
||||||
|
case RemoteProjectileData(CommonFieldDataWithPlacement(pos, deploy), unk2, lim, unk3, unk4, unk5) =>
|
||||||
|
pos.coord mustEqual Vector3(4644.5938f, 5472.0938f, 82.375f)
|
||||||
|
pos.orient mustEqual Vector3(0, 30.9375f, 171.5625f)
|
||||||
|
deploy.faction mustEqual PlanetSideEmpire.TR
|
||||||
|
deploy.bops mustEqual false
|
||||||
|
deploy.alternate mustEqual false
|
||||||
|
deploy.v1 mustEqual true
|
||||||
|
deploy.v2.isEmpty mustEqual true
|
||||||
|
deploy.v3 mustEqual false
|
||||||
|
deploy.v4.isEmpty mustEqual true
|
||||||
|
deploy.v5.isEmpty mustEqual true
|
||||||
|
deploy.guid mustEqual PlanetSideGUID(0)
|
||||||
|
|
||||||
|
unk2 mustEqual 26214
|
||||||
|
lim mustEqual 134
|
||||||
|
unk3 mustEqual FlightPhysics.State4
|
||||||
|
unk4 mustEqual 0
|
||||||
|
unk5 mustEqual 0
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"decode (hunter_seeker_missile_projectile)" in {
|
||||||
|
PacketCoding.DecodePacket(string_hunter_seeker_missile_projectile).require match {
|
||||||
|
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||||
|
len mustEqual 197
|
||||||
|
cls mustEqual ObjectClass.hunter_seeker_missile_projectile
|
||||||
|
guid mustEqual PlanetSideGUID(40619)
|
||||||
|
parent.isDefined mustEqual false
|
||||||
|
data match {
|
||||||
|
case RemoteProjectileData(CommonFieldDataWithPlacement(pos, deploy), unk2, lim, unk3, unk4, unk5) =>
|
||||||
|
pos.coord mustEqual Vector3(3621.3672f, 2701.8438f, 140.85938f)
|
||||||
|
pos.orient mustEqual Vector3(0, 300.9375f, 258.75f)
|
||||||
|
deploy.faction mustEqual PlanetSideEmpire.NC
|
||||||
|
deploy.bops mustEqual false
|
||||||
|
deploy.alternate mustEqual false
|
||||||
|
deploy.v1 mustEqual true
|
||||||
|
deploy.v2.isEmpty mustEqual true
|
||||||
|
deploy.v3 mustEqual false
|
||||||
|
deploy.v4.isEmpty mustEqual true
|
||||||
|
deploy.v5.isEmpty mustEqual true
|
||||||
|
deploy.guid mustEqual PlanetSideGUID(0)
|
||||||
|
|
||||||
|
unk2 mustEqual 39577
|
||||||
|
lim mustEqual 201
|
||||||
|
unk3 mustEqual FlightPhysics.State4
|
||||||
|
unk4 mustEqual 0
|
||||||
|
unk5 mustEqual 0
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (striker_missile_targeting_projectile)" in {
|
||||||
|
val obj = RemoteProjectileData(
|
||||||
|
CommonFieldDataWithPlacement(
|
||||||
|
PlacementData(4644.5938f, 5472.0938f, 82.375f, 0f, 30.9375f, 171.5625f),
|
||||||
|
CommonFieldData(PlanetSideEmpire.TR, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
||||||
|
),
|
||||||
|
26214, 134, FlightPhysics.State4, 0, 0
|
||||||
|
)
|
||||||
|
val msg = ObjectCreateMessage(ObjectClass.striker_missile_targeting_projectile, PlanetSideGUID(40192), obj)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
//pkt mustEqual string_striker_projectile
|
||||||
|
|
||||||
|
pkt.toBitVector.take(132) mustEqual string_striker_projectile.toBitVector.take(132)
|
||||||
|
pkt.toBitVector.drop(133).take(7) mustEqual string_striker_projectile.toBitVector.drop(133).take(7)
|
||||||
|
pkt.toBitVector.drop(141) mustEqual string_striker_projectile.toBitVector.drop(141)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (hunter_seeker_missile_projectile)" in {
|
||||||
|
val obj = RemoteProjectileData(
|
||||||
|
CommonFieldDataWithPlacement(
|
||||||
|
PlacementData(3621.3672f, 2701.8438f, 140.85938f, 0, 300.9375f, 258.75f),
|
||||||
|
CommonFieldData(PlanetSideEmpire.NC, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
||||||
|
),
|
||||||
|
39577, 201, FlightPhysics.State4, 0, 0
|
||||||
|
)
|
||||||
|
val msg = ObjectCreateMessage(ObjectClass.hunter_seeker_missile_projectile, PlanetSideGUID(40619), obj)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
//pkt mustEqual string_hunter_seeker_missile_projectile
|
||||||
|
|
||||||
|
pkt.toBitVector.take(132) mustEqual string_hunter_seeker_missile_projectile.toBitVector.take(132)
|
||||||
|
pkt.toBitVector.drop(133).take(7) mustEqual string_hunter_seeker_missile_projectile.toBitVector.drop(133).take(7)
|
||||||
|
pkt.toBitVector.drop(141) mustEqual string_hunter_seeker_missile_projectile.toBitVector.drop(141)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright (c) 2017 PSForever
|
|
||||||
package game.objectcreate
|
|
||||||
|
|
||||||
import net.psforever.packet.PacketCoding
|
|
||||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
|
|
||||||
import net.psforever.packet.game.objectcreate._
|
|
||||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
|
||||||
import org.specs2.mutable._
|
|
||||||
import scodec.bits._
|
|
||||||
|
|
||||||
class TrackedProjectileDataTest extends Specification {
|
|
||||||
val string_striker_projectile = hex"17 C5000000 A4B 009D 4C129 0CB0A 9814 00 F5 E3 040000666686400"
|
|
||||||
|
|
||||||
"TrackedProjectileData" should {
|
|
||||||
"decode" in {
|
|
||||||
PacketCoding.DecodePacket(string_striker_projectile).require match {
|
|
||||||
case ObjectCreateMessage(len, cls, guid, parent, data) =>
|
|
||||||
len mustEqual 197
|
|
||||||
cls mustEqual ObjectClass.striker_missile_targeting_projectile
|
|
||||||
guid mustEqual PlanetSideGUID(40192)
|
|
||||||
parent.isDefined mustEqual false
|
|
||||||
data match {
|
|
||||||
case TrackedProjectileData(CommonFieldDataWithPlacement(pos, deploy), unk2, unk3) =>
|
|
||||||
pos.coord mustEqual Vector3(4644.5938f, 5472.0938f, 82.375f)
|
|
||||||
pos.orient mustEqual Vector3(0, 30.9375f, 171.5625f)
|
|
||||||
deploy.faction mustEqual PlanetSideEmpire.TR
|
|
||||||
deploy.bops mustEqual false
|
|
||||||
deploy.alternate mustEqual false
|
|
||||||
deploy.v1 mustEqual true
|
|
||||||
deploy.v2.isEmpty mustEqual true
|
|
||||||
deploy.v3 mustEqual false
|
|
||||||
deploy.v4.isEmpty mustEqual true
|
|
||||||
deploy.v5.isEmpty mustEqual true
|
|
||||||
deploy.guid mustEqual PlanetSideGUID(0)
|
|
||||||
|
|
||||||
unk2 mustEqual TrackedProjectile.Striker
|
|
||||||
|
|
||||||
unk3 mustEqual 0
|
|
||||||
case _ =>
|
|
||||||
ko
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
ko
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"encode" in {
|
|
||||||
val obj = TrackedProjectileData(
|
|
||||||
CommonFieldDataWithPlacement(
|
|
||||||
PlacementData(4644.5938f, 5472.0938f, 82.375f, 0f, 30.9375f, 171.5625f),
|
|
||||||
CommonFieldData(PlanetSideEmpire.TR, false, false, true, None, false, None, None, PlanetSideGUID(0))
|
|
||||||
),
|
|
||||||
TrackedProjectile.Striker,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
val msg = ObjectCreateMessage(ObjectClass.striker_missile_targeting_projectile, PlanetSideGUID(40192), obj)
|
|
||||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
|
||||||
|
|
||||||
pkt.toBitVector.take(132) mustEqual string_striker_projectile.toBitVector.take(132)
|
|
||||||
pkt.toBitVector.drop(133).take(7) mustEqual string_striker_projectile.toBitVector.drop(133).take(7)
|
|
||||||
pkt.toBitVector.drop(141) mustEqual string_striker_projectile.toBitVector.drop(141)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -56,6 +56,7 @@ import services.vehicle.support.TurretUpgrader
|
||||||
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
|
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
|
||||||
import services.teamwork.{SquadResponse, SquadService, SquadServiceMessage, SquadServiceResponse, SquadAction => SquadServiceAction}
|
import services.teamwork.{SquadResponse, SquadService, SquadServiceMessage, SquadServiceResponse, SquadAction => SquadServiceAction}
|
||||||
|
|
||||||
|
import scala.collection.concurrent.TrieMap
|
||||||
import scala.collection.mutable.LongMap
|
import scala.collection.mutable.LongMap
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
|
@ -69,6 +70,8 @@ import net.psforever.objects.vehicles.Utility.InternalTelepad
|
||||||
import services.local.support.{HackCaptureActor, RouterTelepadActivation}
|
import services.local.support.{HackCaptureActor, RouterTelepadActivation}
|
||||||
import services.support.SupportActor
|
import services.support.SupportActor
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
class WorldSessionActor extends Actor with MDCContextAware {
|
class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
import WorldSessionActor._
|
import WorldSessionActor._
|
||||||
|
|
||||||
|
|
@ -115,6 +118,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
var whenUsedLastSAKit : Long = 0
|
var whenUsedLastSAKit : Long = 0
|
||||||
var whenUsedLastSSKit : Long = 0
|
var whenUsedLastSSKit : Long = 0
|
||||||
val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](Projectile.RangeUID - Projectile.BaseUID)(None)
|
val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](Projectile.RangeUID - Projectile.BaseUID)(None)
|
||||||
|
val projectilesToCleanUp : Array[Boolean] = Array.fill[Boolean](Projectile.RangeUID - Projectile.BaseUID)(false)
|
||||||
var drawDeloyableIcon : PlanetSideGameObject with Deployable => Unit = RedrawDeployableIcons
|
var drawDeloyableIcon : PlanetSideGameObject with Deployable => Unit = RedrawDeployableIcons
|
||||||
var updateSquad : () => Unit = NoSquadUpdates
|
var updateSquad : () => Unit = NoSquadUpdates
|
||||||
var recentTeleportAttempt : Long = 0
|
var recentTeleportAttempt : Long = 0
|
||||||
|
|
@ -164,6 +168,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
var antChargingTick : Cancellable = DefaultCancellable.obj
|
var antChargingTick : Cancellable = DefaultCancellable.obj
|
||||||
var antDischargingTick : Cancellable = DefaultCancellable.obj
|
var antDischargingTick : Cancellable = DefaultCancellable.obj
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a boolean value into an integer value.
|
* Convert a boolean value into an integer value.
|
||||||
* Use: `true:Int` or `false:Int`
|
* Use: `true:Int` or `false:Int`
|
||||||
|
|
@ -1218,6 +1223,30 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
log.info(s"Received a direct message: $pkt")
|
log.info(s"Received a direct message: $pkt")
|
||||||
sendResponse(pkt)
|
sendResponse(pkt)
|
||||||
|
|
||||||
|
case LoadedRemoteProjectile(projectile_guid, Some(projectile)) =>
|
||||||
|
if(projectile.profile.ExistsOnRemoteClients) {
|
||||||
|
//spawn projectile on other clients
|
||||||
|
val definition = projectile.Definition
|
||||||
|
avatarService ! AvatarServiceMessage(
|
||||||
|
continent.Id,
|
||||||
|
AvatarAction.LoadProjectile(player.GUID, definition.ObjectId, projectile_guid, definition.Packet.ConstructorData(projectile).get)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//immediately slated for deletion?
|
||||||
|
CleanUpRemoteProjectile(projectile.GUID, projectile)
|
||||||
|
|
||||||
|
case LoadedRemoteProjectile(projectile_guid, None) =>
|
||||||
|
continent.GUID(projectile_guid) match {
|
||||||
|
case Some(obj : Projectile) if obj.profile.ExistsOnRemoteClients =>
|
||||||
|
//spawn projectile on other clients
|
||||||
|
val definition = obj.Definition
|
||||||
|
avatarService ! AvatarServiceMessage(
|
||||||
|
continent.Id,
|
||||||
|
AvatarAction.LoadProjectile(player.GUID, definition.ObjectId, projectile_guid, definition.Packet.ConstructorData(obj).get)
|
||||||
|
)
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
|
||||||
case default =>
|
case default =>
|
||||||
log.warn(s"Invalid packet class received: $default from $sender")
|
log.warn(s"Invalid packet class received: $default from $sender")
|
||||||
}
|
}
|
||||||
|
|
@ -1365,6 +1394,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
sendResponse(pkt)
|
sendResponse(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AvatarResponse.GenericObjectAction(object_guid, action_code) =>
|
||||||
|
if(tplayer_guid != guid) {
|
||||||
|
sendResponse(GenericObjectActionMessage(object_guid, action_code))
|
||||||
|
}
|
||||||
|
|
||||||
case AvatarResponse.HitHint(source_guid) =>
|
case AvatarResponse.HitHint(source_guid) =>
|
||||||
if(player.isAlive) {
|
if(player.isAlive) {
|
||||||
sendResponse(HitHint(source_guid, guid))
|
sendResponse(HitHint(source_guid, guid))
|
||||||
|
|
@ -1403,6 +1437,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
sendResponse(pkt)
|
sendResponse(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AvatarResponse.LoadProjectile(pkt) =>
|
||||||
|
if(tplayer_guid != guid) {
|
||||||
|
sendResponse(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
case AvatarResponse.ObjectDelete(item_guid, unk) =>
|
case AvatarResponse.ObjectDelete(item_guid, unk) =>
|
||||||
if(tplayer_guid != guid) {
|
if(tplayer_guid != guid) {
|
||||||
sendResponse(ObjectDeleteMessage(item_guid, unk))
|
sendResponse(ObjectDeleteMessage(item_guid, unk))
|
||||||
|
|
@ -1464,6 +1503,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AvatarResponse.ProjectileExplodes(projectile_guid, projectile) =>
|
||||||
|
sendResponse(ProjectileStateMessage(projectile_guid, projectile.Position, Vector3.Zero, projectile.Orientation, 0, true, PlanetSideGUID(0)))
|
||||||
|
sendResponse(ObjectDeleteMessage(projectile_guid, 2))
|
||||||
|
|
||||||
|
case AvatarResponse.ProjectileAutoLockAwareness(mode) =>
|
||||||
|
sendResponse(GenericActionMessage(mode))
|
||||||
|
|
||||||
|
case AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) =>
|
||||||
|
if(tplayer_guid != guid) {
|
||||||
|
sendResponse(ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid))
|
||||||
|
// continent.GUID(projectile_guid) match {
|
||||||
|
// case Some(obj) =>
|
||||||
|
// val definition = obj.Definition
|
||||||
|
// sendResponse(ObjectCreateMessage(definition.ObjectId, projectile_guid, definition.Packet.ConstructorData(obj).get))
|
||||||
|
// case _ => ;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
case AvatarResponse.PutDownFDU(target) =>
|
case AvatarResponse.PutDownFDU(target) =>
|
||||||
if(tplayer_guid != guid) {
|
if(tplayer_guid != guid) {
|
||||||
sendResponse(GenericObjectActionMessage(target, 212))
|
sendResponse(GenericObjectActionMessage(target, 212))
|
||||||
|
|
@ -3457,6 +3514,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
//all head features, faction, and sex also match that test character
|
//all head features, faction, and sex also match that test character
|
||||||
import net.psforever.objects.GlobalDefinitions._
|
import net.psforever.objects.GlobalDefinitions._
|
||||||
import net.psforever.types.CertificationType._
|
import net.psforever.types.CertificationType._
|
||||||
|
|
||||||
val faction = PlanetSideEmpire.VS
|
val faction = PlanetSideEmpire.VS
|
||||||
val avatar = new Avatar(41605313L+sessionId, s"TestCharacter$sessionId", faction, CharacterGender.Female, 41, CharacterVoice.Voice1)
|
val avatar = new Avatar(41605313L+sessionId, s"TestCharacter$sessionId", faction, CharacterGender.Female, 41, CharacterVoice.Voice1)
|
||||||
avatar.Certifications += StandardAssault
|
avatar.Certifications += StandardAssault
|
||||||
|
|
@ -3855,52 +3913,70 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
self ! SetCurrentAvatar(player)
|
self ! SetCurrentAvatar(player)
|
||||||
|
|
||||||
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, jump_thrust, is_cloaking, unk5, unk6) =>
|
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, jump_thrust, is_cloaking, unk5, unk6) =>
|
||||||
if(deadState == DeadState.Alive) {
|
val isMoving = WorldEntity.isMoving(vel)
|
||||||
if (timeDL != 0) {
|
//implants and stamina management start
|
||||||
if (System.currentTimeMillis() - timeDL > 500) {
|
val implantsAreActive = avatar.Implants(0).Active || avatar.Implants(1).Active
|
||||||
player.Stamina = player.Stamina - 1
|
val staminaBefore = player.Stamina
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
val hadStaminaBefore = staminaBefore > 0
|
||||||
timeDL = System.currentTimeMillis()
|
val hasStaminaAfter = if(deadState == DeadState.Alive) {
|
||||||
|
if(implantsAreActive && hadStaminaBefore) {
|
||||||
|
val time = System.currentTimeMillis()
|
||||||
|
if(timeDL != 0) {
|
||||||
|
val duration = time - timeSurge
|
||||||
|
if(duration > 500) {
|
||||||
|
val units = (duration / 500).toInt
|
||||||
|
player.Stamina = player.Stamina - units
|
||||||
|
timeDL += units * 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(timeSurge != 0) {
|
||||||
|
val duration = time - timeSurge
|
||||||
|
val period = player.ExoSuit match {
|
||||||
|
case ExoSuitType.Agile => 500
|
||||||
|
case ExoSuitType.Reinforced => 333
|
||||||
|
case ExoSuitType.Infiltration => 1000
|
||||||
|
case ExoSuitType.Standard => 1000
|
||||||
|
case _ => 1
|
||||||
|
}
|
||||||
|
if(duration > period) {
|
||||||
|
val units = (duration / period).toInt
|
||||||
|
player.Stamina = player.Stamina - units
|
||||||
|
timeSurge += period * units
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (timeSurge != 0) {
|
//if the player lost all stamina this turn (had stamina at the start), do not renew 1 stamina
|
||||||
if (System.currentTimeMillis() - timeSurge > 500 && player.ExoSuit == ExoSuitType.Agile) {
|
if(!isMoving && (if(player.Stamina > 0) player.Stamina < player.MaxStamina else !hadStaminaBefore)) {
|
||||||
player.Stamina = player.Stamina - 1
|
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
|
||||||
timeSurge = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
else if (System.currentTimeMillis() - timeSurge > 333 && player.ExoSuit == ExoSuitType.Reinforced) {
|
|
||||||
player.Stamina = player.Stamina - 1
|
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
|
||||||
timeSurge = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
else if (System.currentTimeMillis() - timeSurge > 1000 && (player.ExoSuit == ExoSuitType.Infiltration || player.ExoSuit == ExoSuitType.Standard)) {
|
|
||||||
player.Stamina = player.Stamina - 1
|
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
|
||||||
timeSurge = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (player.Stamina == 0) {
|
|
||||||
if (avatar.Implants(0).Active) {
|
|
||||||
avatar.Implants(0).Active = false
|
|
||||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(0).id * 2))
|
|
||||||
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 0, 0))
|
|
||||||
timeDL = 0
|
|
||||||
}
|
|
||||||
if (avatar.Implants(1).Active) {
|
|
||||||
avatar.Implants(1).Active = false
|
|
||||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(1).id * 2))
|
|
||||||
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 1, 0))
|
|
||||||
timeSurge = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val isMoving = WorldEntity.isMoving(vel)
|
|
||||||
if (!isMoving && player.Stamina < player.MaxStamina) {
|
|
||||||
player.Stamina = player.Stamina + 1
|
player.Stamina = player.Stamina + 1
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
player.Stamina > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
timeDL = 0
|
||||||
|
timeSurge = 0
|
||||||
|
false
|
||||||
|
}
|
||||||
|
if(staminaBefore != player.Stamina) { //stamina changed
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, player.Stamina))
|
||||||
|
}
|
||||||
|
if(implantsAreActive && !hasStaminaAfter) { //implants deactivated at 0 stamina
|
||||||
|
if(avatar.Implants(0).Active) {
|
||||||
|
avatar.Implants(0).Active = false
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(0).id * 2))
|
||||||
|
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 0, 0))
|
||||||
|
timeDL = 0
|
||||||
|
}
|
||||||
|
if(avatar.Implants(1).Active) {
|
||||||
|
avatar.Implants(1).Active = false
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(1).id * 2))
|
||||||
|
sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid), ImplantAction.Activation, 1, 0))
|
||||||
|
timeSurge = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//implants and stamina management finish
|
||||||
player.Position = pos
|
player.Position = pos
|
||||||
player.Velocity = vel
|
player.Velocity = vel
|
||||||
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
|
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
|
||||||
|
|
@ -4002,8 +4078,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case msg @ VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) =>
|
case msg @ VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) =>
|
||||||
//log.info(s"VehicleSubState: $vehicle_guid, $player_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2")
|
//log.info(s"VehicleSubState: $vehicle_guid, $player_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2")
|
||||||
|
|
||||||
case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vector, unk1, unk2, unk3, unk4, time_alive) =>
|
case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) =>
|
||||||
//log.info("ProjectileState: " + msg)
|
//log.trace(s"ProjectileState: $msg")
|
||||||
|
val index = projectile_guid.guid - Projectile.BaseUID
|
||||||
|
projectiles(index) match {
|
||||||
|
case Some(projectile) if projectile.HasGUID =>
|
||||||
|
val projectileGlobalUID = projectile.GUID
|
||||||
|
projectile.Position = shot_pos
|
||||||
|
projectile.Orientation = shot_orient
|
||||||
|
projectile.Velocity = shot_vel
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileState(player.GUID, projectileGlobalUID, shot_pos, shot_vel, shot_orient, seq, end, target_guid))
|
||||||
|
case _ if seq == 0 =>
|
||||||
|
/* missing the first packet in the sequence is permissible */
|
||||||
|
case _ =>
|
||||||
|
log.warn(s"ProjectileState: constructed projectile ${projectile_guid.guid} can not be found")
|
||||||
|
}
|
||||||
|
|
||||||
case msg @ ReleaseAvatarRequestMessage() =>
|
case msg @ ReleaseAvatarRequestMessage() =>
|
||||||
log.info(s"ReleaseAvatarRequest: ${player.GUID} on ${continent.Id} has released")
|
log.info(s"ReleaseAvatarRequest: ${player.GUID} on ${continent.Id} has released")
|
||||||
|
|
@ -4278,14 +4367,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ ChangeFireStateMessage_Start(item_guid) =>
|
case msg @ ChangeFireStateMessage_Start(item_guid) =>
|
||||||
log.info("ChangeFireState_Start: " + msg)
|
log.trace("ChangeFireState_Start: " + msg)
|
||||||
if(shooting.isEmpty) {
|
if(shooting.isEmpty) {
|
||||||
FindEquipment match {
|
FindEquipment match {
|
||||||
case Some(tool : Tool) =>
|
case Some(tool : Tool) =>
|
||||||
if(tool.Magazine > 0 || prefire.contains(item_guid)) {
|
if(tool.Magazine > 0 || prefire.contains(item_guid)) {
|
||||||
prefire = None
|
prefire = None
|
||||||
shooting = Some(item_guid)
|
shooting = Some(item_guid)
|
||||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(player.GUID, item_guid))
|
//special case - suppress the decimator's alternate fire mode, by projectile
|
||||||
|
if(tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(player.GUID, item_guid))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.warn(s"ChangeFireState_Start: ${tool.Definition.Name} magazine is empty before trying to shoot bullet")
|
log.warn(s"ChangeFireState_Start: ${tool.Definition.Name} magazine is empty before trying to shoot bullet")
|
||||||
|
|
@ -4301,7 +4393,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ ChangeFireStateMessage_Stop(item_guid) =>
|
case msg @ ChangeFireStateMessage_Stop(item_guid) =>
|
||||||
log.info("ChangeFireState_Stop: " + msg)
|
log.trace("ChangeFireState_Stop: " + msg)
|
||||||
prefire = None
|
prefire = None
|
||||||
val weapon : Option[Equipment] = if(shooting.contains(item_guid)) {
|
val weapon : Option[Equipment] = if(shooting.contains(item_guid)) {
|
||||||
shooting = None
|
shooting = None
|
||||||
|
|
@ -4309,13 +4401,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
FindEquipment
|
FindEquipment
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//some weapons, e.g., the decimator, do not send a ChangeFireState_Start on the last shot
|
|
||||||
FindEquipment match {
|
FindEquipment match {
|
||||||
case Some(tool) =>
|
case Some(tool : Tool) =>
|
||||||
if(tool.Definition == GlobalDefinitions.phoenix) {
|
//the decimator does not send a ChangeFireState_Start on the last shot
|
||||||
|
if(tool.Definition == GlobalDefinitions.phoenix &&
|
||||||
|
tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile) {
|
||||||
|
//suppress the decimator's alternate fire mode, however
|
||||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(player.GUID, item_guid))
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(player.GUID, item_guid))
|
||||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, item_guid))
|
|
||||||
}
|
}
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, item_guid))
|
||||||
|
Some(tool)
|
||||||
|
case Some(tool) => //permissible, for now
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, item_guid))
|
||||||
Some(tool)
|
Some(tool)
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"ChangeFireState_Stop: received an unexpected message about $item_guid")
|
log.warn(s"ChangeFireState_Stop: received an unexpected message about $item_guid")
|
||||||
|
|
@ -4474,7 +4571,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
//log.info("AvatarJump: " + msg)
|
//log.info("AvatarJump: " + msg)
|
||||||
player.Stamina = player.Stamina - 10
|
player.Stamina = player.Stamina - 10
|
||||||
if(player.Stamina < 0) player.Stamina = 0
|
if(player.Stamina < 0) player.Stamina = 0
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, player.Stamina))
|
||||||
|
|
||||||
case msg @ ZipLineMessage(player_guid,origin_side,action,id,pos) =>
|
case msg @ ZipLineMessage(player_guid,origin_side,action,id,pos) =>
|
||||||
log.info("ZipLineMessage: " + msg)
|
log.info("ZipLineMessage: " + msg)
|
||||||
|
|
@ -4533,6 +4630,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
projectile.Miss()
|
projectile.Miss()
|
||||||
|
if(projectile.profile.ExistsOnRemoteClients && projectile.HasGUID) {
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile))
|
||||||
|
taskResolver ! UnregisterProjectile(projectile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
log.warn(s"RequestDestroy: projectile ${object_guid.guid} has never been fired")
|
log.warn(s"RequestDestroy: projectile ${object_guid.guid} has never been fired")
|
||||||
|
|
@ -4682,7 +4783,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
if (avatar.Implant(slot).id == 3) {
|
if (avatar.Implant(slot).id == 3) {
|
||||||
timeDL = System.currentTimeMillis()
|
timeDL = System.currentTimeMillis()
|
||||||
player.Stamina = player.Stamina - 3
|
player.Stamina = player.Stamina - 3
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, player.Stamina))
|
||||||
}
|
}
|
||||||
if (avatar.Implant(slot).id == 9) timeSurge = System.currentTimeMillis()
|
if (avatar.Implant(slot).id == 9) timeSurge = System.currentTimeMillis()
|
||||||
} else if(action == ImplantAction.Activation && status == 0) { //desactive
|
} else if(action == ImplantAction.Activation && status == 0) { //desactive
|
||||||
|
|
@ -5404,7 +5505,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
|
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
|
||||||
log.info("WeaponFire: " + msg)
|
log.info(s"WeaponFire: $msg")
|
||||||
FindContainedWeapon match {
|
FindContainedWeapon match {
|
||||||
case (Some(obj), Some(tool : Tool)) =>
|
case (Some(obj), Some(tool : Tool)) =>
|
||||||
if(tool.Magazine <= 0) { //safety: enforce ammunition depletion
|
if(tool.Magazine <= 0) { //safety: enforce ammunition depletion
|
||||||
|
|
@ -5420,11 +5521,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
else { //shooting
|
else { //shooting
|
||||||
if (tool.FireModeIndex == 1 && (tool.Definition.Name == "anniversary_guna" || tool.Definition.Name == "anniversary_gun" || tool.Definition.Name == "anniversary_gunb")) {
|
if (tool.FireModeIndex == 1 && (tool.Definition.Name == "anniversary_guna" || tool.Definition.Name == "anniversary_gun" || tool.Definition.Name == "anniversary_gunb")) {
|
||||||
player.Stamina = 0
|
player.Stamina = 0
|
||||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 2, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
prefire = shooting.orElse(Some(weapon_guid))
|
prefire = shooting.orElse(Some(weapon_guid))
|
||||||
tool.Discharge
|
tool.Discharge //always
|
||||||
val projectileIndex = projectile_guid.guid - Projectile.BaseUID
|
val projectileIndex = projectile_guid.guid - Projectile.BaseUID
|
||||||
val projectilePlace = projectiles(projectileIndex)
|
val projectilePlace = projectiles(projectileIndex)
|
||||||
if(projectilePlace match {
|
if(projectilePlace match {
|
||||||
|
|
@ -5435,7 +5536,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
val (angle, attribution, acceptableDistanceToOwner) = obj match {
|
val (angle, attribution, acceptableDistanceToOwner) = obj match {
|
||||||
case p : Player =>
|
case p : Player =>
|
||||||
(SimpleWorldEntity.validateOrientationEntry(p.Orientation + Vector3.z(p.FacingYawUpper)), tool.Definition.ObjectId, 10f)
|
(SimpleWorldEntity.validateOrientationEntry(p.Orientation + Vector3.z(p.FacingYawUpper)), tool.Definition.ObjectId, 10f + (if(p.Velocity.nonEmpty) { 5f } else { 0f }))
|
||||||
case v : Vehicle if v.Definition.CanFly =>
|
case v : Vehicle if v.Definition.CanFly =>
|
||||||
(tool.Orientation, obj.Definition.ObjectId, 1000f) //TODO this is too simplistic to find proper angle
|
(tool.Orientation, obj.Definition.ObjectId, 1000f) //TODO this is too simplistic to find proper angle
|
||||||
case _ : Vehicle =>
|
case _ : Vehicle =>
|
||||||
|
|
@ -5445,8 +5546,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
val distanceToOwner = Vector3.DistanceSquared(shot_origin, player.Position)
|
val distanceToOwner = Vector3.DistanceSquared(shot_origin, player.Position)
|
||||||
if(distanceToOwner <= acceptableDistanceToOwner) {
|
if(distanceToOwner <= acceptableDistanceToOwner) {
|
||||||
projectiles(projectileIndex) =
|
val projectile_info = tool.Projectile
|
||||||
Some(Projectile(tool.Projectile, tool.Definition, tool.FireMode, player, attribution, shot_origin, angle))
|
val projectile = Projectile(projectile_info, tool.Definition, tool.FireMode, player, attribution, shot_origin, angle)
|
||||||
|
projectiles(projectileIndex) = Some(projectile)
|
||||||
|
if(projectile_info.ExistsOnRemoteClients) {
|
||||||
|
log.trace(s"WeaponFireMessage: ${projectile_info.Name} is a remote projectile")
|
||||||
|
taskResolver ! (if(projectile.HasGUID) {
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile))
|
||||||
|
ReregisterProjectile(projectile)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RegisterProjectile(projectile)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
projectilesToCleanUp(projectileIndex) = false
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.warn(s"WeaponFireMessage: $player's ${tool.Definition.Name} projectile is too far from owner position at time of discharge ($distanceToOwner > $acceptableDistanceToOwner); suspect")
|
log.warn(s"WeaponFireMessage: $player's ${tool.Definition.Name} projectile is too far from owner position at time of discharge ($distanceToOwner > $acceptableDistanceToOwner); suspect")
|
||||||
|
|
@ -5458,6 +5571,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) =>
|
case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) =>
|
||||||
log.info("Lazing position: " + pos2.toString)
|
log.info("Lazing position: " + pos2.toString)
|
||||||
|
|
||||||
|
case msg @ ObjectDetectedMessage(guid1, guid2, unk, targets) =>
|
||||||
|
//log.info(s"Detection: $msg")
|
||||||
|
FindWeapon match {
|
||||||
|
case Some(weapon) if weapon.Projectile.AutoLock =>
|
||||||
|
//projectile with auto-lock instigates a warning on the target
|
||||||
|
val detectedTargets = FindDetectedProjectileTargets(targets)
|
||||||
|
if(detectedTargets.nonEmpty) {
|
||||||
|
val mode = 7 + (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile)
|
||||||
|
detectedTargets.foreach { target =>
|
||||||
|
avatarService ! AvatarServiceMessage(target, AvatarAction.ProjectileAutoLockAwareness(mode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
|
||||||
case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
|
case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
|
||||||
log.info(s"Hit: $msg")
|
log.info(s"Hit: $msg")
|
||||||
(hit_info match {
|
(hit_info match {
|
||||||
|
|
@ -5482,26 +5610,42 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
|
|
||||||
case msg @ SplashHitMessage(seq_time, projectile_guid, explosion_pos, direct_victim_uid, unk3, projectile_vel, unk4, targets) =>
|
case msg @ SplashHitMessage(seq_time, projectile_guid, explosion_pos, direct_victim_uid, unk3, projectile_vel, unk4, targets) =>
|
||||||
log.info(s"Splash: $msg")
|
log.info(s"Splash: $msg")
|
||||||
continent.GUID(direct_victim_uid) match {
|
FindProjectileEntry(projectile_guid) match {
|
||||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(projectile) =>
|
||||||
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Splash, target, target.Position) match {
|
projectile.Position = explosion_pos
|
||||||
case Some(projectile) =>
|
projectile.Velocity = projectile_vel
|
||||||
HandleDealingDamage(target, projectile)
|
continent.GUID(direct_victim_uid) match {
|
||||||
case None => ;
|
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
|
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position) match {
|
||||||
|
case Some(projectile) =>
|
||||||
|
HandleDealingDamage(target, projectile)
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
case _ => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
targets.foreach(elem => {
|
||||||
}
|
continent.GUID(elem.uid) match {
|
||||||
targets.foreach(elem => {
|
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
continent.GUID(elem.uid) match {
|
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos) match {
|
||||||
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(projectile) =>
|
||||||
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Splash, target, explosion_pos) match {
|
HandleDealingDamage(target, projectile)
|
||||||
case Some(projectile) =>
|
case None => ;
|
||||||
HandleDealingDamage(target, projectile)
|
}
|
||||||
case None => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
})
|
||||||
}
|
if(projectile.profile.ExistsOnRemoteClients && projectile.HasGUID) {
|
||||||
})
|
//cleanup
|
||||||
|
val localIndex = projectile_guid.guid - Projectile.BaseUID
|
||||||
|
if(projectile.HasGUID) {
|
||||||
|
CleanUpRemoteProjectile(projectile.GUID, projectile, localIndex)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
projectilesToCleanUp(localIndex) = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
|
||||||
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
|
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
|
||||||
log.info(s"Lash: $msg")
|
log.info(s"Lash: $msg")
|
||||||
|
|
@ -6068,6 +6212,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}, List(GUIDTask.RegisterAvatar(driver)(continent.GUID), GUIDTask.RegisterVehicle(obj)(continent.GUID)))
|
}, List(GUIDTask.RegisterAvatar(driver)(continent.GUID), GUIDTask.RegisterVehicle(obj)(continent.GUID)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct tasking that adds a completed but unregistered projectile into the scene.
|
||||||
|
* After the projectile is registered to the curent zone's global unique identifier system,
|
||||||
|
* all connected clients save for the one that registered it will be informed about the projectile's "creation."
|
||||||
|
* @param obj the projectile to be registered
|
||||||
|
* @return a `TaskResolver.GiveTask` message
|
||||||
|
*/
|
||||||
|
def RegisterProjectile(obj : Projectile) : TaskResolver.GiveTask = {
|
||||||
|
val definition = obj.Definition
|
||||||
|
TaskResolver.GiveTask(
|
||||||
|
new Task() {
|
||||||
|
private val globalProjectile = obj
|
||||||
|
private val localAnnounce = self
|
||||||
|
|
||||||
|
override def isComplete : Task.Resolution.Value = {
|
||||||
|
if(globalProjectile.HasGUID) {
|
||||||
|
Task.Resolution.Success
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Task.Resolution.Incomplete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def Execute(resolver : ActorRef) : Unit = {
|
||||||
|
localAnnounce ! LoadedRemoteProjectile(globalProjectile.GUID, Some(globalProjectile))
|
||||||
|
resolver ! scala.util.Success(this)
|
||||||
|
}
|
||||||
|
}, List(GUIDTask.RegisterObjectTask(obj)(continent.GUID))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def UnregisterDrivenVehicle(obj : Vehicle, driver : Player) : TaskResolver.GiveTask = {
|
def UnregisterDrivenVehicle(obj : Vehicle, driver : Player) : TaskResolver.GiveTask = {
|
||||||
TaskResolver.GiveTask(
|
TaskResolver.GiveTask(
|
||||||
new Task() {
|
new Task() {
|
||||||
|
|
@ -6089,6 +6264,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}, List(GUIDTask.UnregisterAvatar(driver)(continent.GUID), GUIDTask.UnregisterVehicle(obj)(continent.GUID)))
|
}, List(GUIDTask.UnregisterAvatar(driver)(continent.GUID), GUIDTask.UnregisterVehicle(obj)(continent.GUID)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct tasking that removes a formerly complete and currently registered projectile from the scene.
|
||||||
|
* After the projectile is unregistered from the curent zone's global unique identifier system,
|
||||||
|
* all connected clients save for the one that registered it will be informed about the projectile's "destruction."
|
||||||
|
* @param obj the projectile to be unregistered
|
||||||
|
* @return a `TaskResolver.GiveTask` message
|
||||||
|
*/
|
||||||
|
def UnregisterProjectile(obj : Projectile) : TaskResolver.GiveTask = {
|
||||||
|
TaskResolver.GiveTask(
|
||||||
|
new Task() {
|
||||||
|
private val globalProjectile = obj
|
||||||
|
private val localAnnounce = avatarService
|
||||||
|
private val localMsg = AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, obj.GUID, 2))
|
||||||
|
|
||||||
|
override def isComplete : Task.Resolution.Value = {
|
||||||
|
if(!globalProjectile.HasGUID) {
|
||||||
|
Task.Resolution.Success
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Task.Resolution.Incomplete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def Execute(resolver : ActorRef) : Unit = {
|
||||||
|
localAnnounce ! localMsg
|
||||||
|
resolver ! scala.util.Success(this)
|
||||||
|
}
|
||||||
|
}, List(GUIDTask.UnregisterObjectTask(obj)(continent.GUID))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct tasking that removes the `Equipment` to `target`.
|
* Construct tasking that removes the `Equipment` to `target`.
|
||||||
* @param target what object that contains the `Equipment`
|
* @param target what object that contains the `Equipment`
|
||||||
|
|
@ -6131,6 +6337,30 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the projectile object is unregistered, register it.
|
||||||
|
* If the projectile object is already registered, unregister it and then register it again.
|
||||||
|
* @see `RegisterProjectile(Projectile)`
|
||||||
|
* @see `UnregisterProjectile(Projectile)`
|
||||||
|
* @param obj the projectile to be registered (a second time?)
|
||||||
|
* @return a `TaskResolver.GiveTask` message
|
||||||
|
*/
|
||||||
|
def ReregisterProjectile(obj : Projectile) : TaskResolver.GiveTask = {
|
||||||
|
val reg = RegisterProjectile(obj)
|
||||||
|
if(obj.HasGUID) {
|
||||||
|
TaskResolver.GiveTask(
|
||||||
|
reg.task,
|
||||||
|
List(TaskResolver.GiveTask(
|
||||||
|
reg.subs(0).task,
|
||||||
|
List(UnregisterProjectile(obj))
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* After some subtasking is completed, draw a particular slot, as if an `ObjectHeldMessage` packet was sent/received.<br>
|
* After some subtasking is completed, draw a particular slot, as if an `ObjectHeldMessage` packet was sent/received.<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
|
@ -7794,7 +8024,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info(s"AvatarCreate (vehicle): $guid -> $data")
|
//log.info(s"AvatarCreate (vehicle): $guid -> $data")
|
||||||
//player, passenger
|
//player, passenger
|
||||||
AvatarCreateInVehicle(player, vehicle, seat)
|
AvatarCreateInVehicle(player, vehicle, seat)
|
||||||
|
|
||||||
|
|
@ -7805,7 +8035,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
val guid = player.GUID
|
val guid = player.GUID
|
||||||
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
|
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
|
||||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.LoadPlayer(guid, ObjectClass.avatar, guid, packet.ConstructorData(player).get, None))
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.LoadPlayer(guid, ObjectClass.avatar, guid, packet.ConstructorData(player).get, None))
|
||||||
log.info(s"AvatarCreate: $guid -> $data")
|
//log.info(s"AvatarCreate: $guid -> $data")
|
||||||
|
log.trace(s"AvatarCreate: ${player.Name}")
|
||||||
}
|
}
|
||||||
continent.Population ! Zone.Population.Spawn(avatar, player)
|
continent.Population ! Zone.Population.Spawn(avatar, player)
|
||||||
//cautious redundancy
|
//cautious redundancy
|
||||||
|
|
@ -7901,7 +8132,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
avatarService ! AvatarServiceMessage(vehicle.Continent, AvatarAction.LoadPlayer(guid, pdef.ObjectId, guid, pdef.Packet.ConstructorData(player).get, Some(parent)))
|
avatarService ! AvatarServiceMessage(vehicle.Continent, AvatarAction.LoadPlayer(guid, pdef.ObjectId, guid, pdef.Packet.ConstructorData(player).get, Some(parent)))
|
||||||
AccessContents(vehicle)
|
AccessContents(vehicle)
|
||||||
UpdateWeaponAtSeatPosition(vehicle, seat)
|
UpdateWeaponAtSeatPosition(vehicle, seat)
|
||||||
log.info(s"AvatarCreateInVehicle: $guid -> $data")
|
//log.info(s"AvatarCreateInVehicle: $guid -> $data")
|
||||||
|
log.trace(s"AvatarCreateInVehicle: ${player.Name} in ${vehicle.Definition.Name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -7978,7 +8210,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the corpse has been well-looted, it has no items in its primary holsters nor any items in its inventory.
|
* If the corpse has been well-lootedP, it has no items in its primary holsters nor any items in its inventory.
|
||||||
* @param obj the corpse
|
* @param obj the corpse
|
||||||
* @return `true`, if the `obj` is actually a corpse and has no objects in its holsters or backpack;
|
* @return `true`, if the `obj` is actually a corpse and has no objects in its holsters or backpack;
|
||||||
* `false`, otherwise
|
* `false`, otherwise
|
||||||
|
|
@ -8331,9 +8563,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
|
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
|
||||||
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
|
|
||||||
* The internal copy of the projectile is retained as merely `Resolved`
|
|
||||||
* while the observed projectile is promoted to the suggested resolution status.
|
|
||||||
* @param projectile the projectile object
|
* @param projectile the projectile object
|
||||||
* @param index where the projectile was found
|
* @param index where the projectile was found
|
||||||
* @param resolution the resolution status to promote the projectile
|
* @param resolution the resolution status to promote the projectile
|
||||||
|
|
@ -8344,8 +8573,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
log.error(s"expected projectile could not be found at $index; can not resolve")
|
log.error(s"expected projectile could not be found at $index; can not resolve")
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
else if(projectile.isMiss) {
|
else {
|
||||||
log.error(s"expected projectile at $index was already counted as a missed shot; can not resolve any further")
|
ResolveProjectileEntry(projectile, resolution, target, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
|
||||||
|
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
|
||||||
|
* The internal copy of the projectile is retained as merely `Resolved`
|
||||||
|
* while the observed projectile is promoted to the suggested resolution status.
|
||||||
|
* @param projectile the projectile object
|
||||||
|
* @param resolution the resolution status to promote the projectile
|
||||||
|
* @return a copy of the projectile
|
||||||
|
*/
|
||||||
|
def ResolveProjectileEntry(projectile : Projectile, resolution : ProjectileResolution.Value, target : PlanetSideGameObject with FactionAffinity with Vitality, pos : Vector3) : Option[ResolvedProjectile] = {
|
||||||
|
if(projectile.isMiss) {
|
||||||
|
log.error("expected projectile was already counted as a missed shot; can not resolve any further")
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -9903,6 +10147,70 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
squadUpdateCounter = (squadUpdateCounter + 1) % queuedSquadActions.length
|
squadUpdateCounter = (squadUpdateCounter + 1) % queuedSquadActions.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main purpose of this method is to determine which targets will receive "locked on" warnings from remote projectiles.
|
||||||
|
* For a given series of globally unique identifiers, indicating targets,
|
||||||
|
* and that may include mounted elements (players),
|
||||||
|
* estimate a series of channel names for communication with the vulnerable targets.
|
||||||
|
* @param targets the globally unique identifiers of the immediate detected targets
|
||||||
|
* @return channels names that allow direct communication to specific realized targets
|
||||||
|
*/
|
||||||
|
def FindDetectedProjectileTargets(targets : Iterable[PlanetSideGUID]) : Iterable[String] = {
|
||||||
|
targets
|
||||||
|
.map { continent.GUID }
|
||||||
|
.flatMap {
|
||||||
|
case Some(obj : Vehicle) if !obj.Cloaked =>
|
||||||
|
//TODO hint: vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.ProjectileAutoLockAwareness(mode))
|
||||||
|
obj.Seats.values.collect { case seat if seat.isOccupied => seat.Occupant.get.Name }
|
||||||
|
case Some(obj : Mountable) =>
|
||||||
|
obj.Seats.values.collect { case seat if seat.isOccupied => seat.Occupant.get.Name }
|
||||||
|
case Some(obj : Player) if obj.ExoSuit == ExoSuitType.MAX =>
|
||||||
|
Seq(obj.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a given registered remote projectile, perform all the actions necessary to properly dispose of it.
|
||||||
|
* Those actions involve:
|
||||||
|
* informing that the projectile should explode,
|
||||||
|
* unregistering the projectile's globally unique identifier,
|
||||||
|
* and managing the projectiles's local status information.
|
||||||
|
* @see `CleanUpRemoteProjectile(PlanetSideGUID, Projectile, Int)`
|
||||||
|
* @param projectile_guid the globally unique identifier of the projectile
|
||||||
|
* @param projectile the projectile
|
||||||
|
*/
|
||||||
|
def CleanUpRemoteProjectile(projectile_guid : PlanetSideGUID, projectile : Projectile) : Unit = {
|
||||||
|
projectiles.indexWhere({
|
||||||
|
case Some(p) => p eq projectile
|
||||||
|
case None => false
|
||||||
|
}) match {
|
||||||
|
case -1 => ; //required catch
|
||||||
|
case index if projectilesToCleanUp(index) =>
|
||||||
|
CleanUpRemoteProjectile(projectile_guid, projectile, index)
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a given registered remote projectile, perform all the actions necessary to properly dispose of it.
|
||||||
|
* Those actions involve:
|
||||||
|
* informing that the projectile should explode,
|
||||||
|
* unregistering the projectile's globally unique identifier,
|
||||||
|
* and managing the projectiles's local status information.
|
||||||
|
* @param projectile_guid the globally unique identifier of the projectile
|
||||||
|
* @param projectile the projectile
|
||||||
|
* @param local_index an index of the absolute sequence of the projectile, for internal lists
|
||||||
|
*/
|
||||||
|
def CleanUpRemoteProjectile(projectile_guid : PlanetSideGUID, projectile : Projectile, local_index : Int) : Unit = {
|
||||||
|
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ProjectileExplodes(player.GUID, projectile_guid, projectile))
|
||||||
|
taskResolver ! UnregisterProjectile(projectile)
|
||||||
|
projectiles(local_index) match {
|
||||||
|
case Some(obj) if !obj.isResolved => obj.Miss
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
projectilesToCleanUp(local_index) = false
|
||||||
|
}
|
||||||
|
|
||||||
def failWithError(error : String) = {
|
def failWithError(error : String) = {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
sendResponse(ConnectionClose())
|
sendResponse(ConnectionClose())
|
||||||
|
|
@ -10066,4 +10374,6 @@ object WorldSessionActor {
|
||||||
private final case class NtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID)
|
private final case class NtuDischarging(tplayer: Player, vehicle: Vehicle, silo_guid: PlanetSideGUID)
|
||||||
|
|
||||||
private final case class FinalizeDeployable(obj : PlanetSideGameObject with Deployable, tool : ConstructionItem, index : Int)
|
private final case class FinalizeDeployable(obj : PlanetSideGameObject with Deployable, tool : ConstructionItem, index : Int)
|
||||||
|
|
||||||
|
private final case class LoadedRemoteProjectile(projectile_guid : PlanetSideGUID, projectile : Option[Projectile])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue