Last Infantry Weapons (#987)

* preparations for deploying oicw little buddy projectiles

* oicw little buddy projectiles spawn and animate properly, but damage dealing is inconclusive

* radiator clouds cause damage to infantry health

* oicw little buddy projectiles do damage upon detonation; different descent pattern; projectile types given own Enumeration

* proximity terminals for vehicle actions no longer need to use the vehicle event system as a middleman for making changes

* redid the workflow of the proximity terminal resolution so that it avoids SessionActor as much as is possible; this may be a mistake, but my future self will pay the price instead

* changed the timing and the angles of the little buddy explosions; fixed proximity terminal tests
This commit is contained in:
Fate-JH 2022-03-27 19:57:32 -04:00 committed by GitHub
parent 2b58d126b5
commit 0d8c717b73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1092 additions and 585 deletions

View file

@ -12,7 +12,7 @@ ignore:
- "src/main/scala/net/psforever/objects/ObjectType.scala"
- "src/main/scala/net/psforever/objects/avatar/Avatars.scala"
- "src/main/scala/net/psforever/objects/ballistics/DamageResolution.scala"
- "src/main/scala/net/psforever/objects/ballistics/Projectiles.scala"
- "src/main/scala/net/psforever/objects/ballistics/Projectiles.Types.scala"
- "src/main/scala/net/psforever/objects/equipment/Ammo.scala"
- "src/main/scala/net/psforever/objects/equipment/CItem.scala"
- "src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala"

View file

@ -659,8 +659,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case Terminal.TerminalMessage(tplayer, msg, order) =>
HandleTerminalMessage(tplayer, msg, order)
case ProximityUnit.Action(term, target) =>
SelectProximityUnitBehavior(term, target)
case ProximityUnit.Action(_, _) => ;
case ProximityUnit.StopAction(term, target) =>
LocalStopUsingProximityUnit(term, target)
case VehicleServiceResponse(toChannel, guid, reply) =>
HandleVehicleServiceResponse(toChannel, guid, reply)
@ -5200,7 +5202,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
case msg @ GenericObjectActionAtPositionMessage(object_guid, _, _) =>
//log.info(s"$msg")
ValidObject(object_guid, decorator = "GenericObjectActionAtPosition") match {
case Some(tool: Tool) if GlobalDefinitions.isBattleFrameNTUSiphon(tool.Definition) =>
FindContainedWeapon match {
@ -5208,7 +5209,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
vehicle.Actor ! SpecialEmp.Burst()
case _ => ;
}
case _ => ;
case _ =>
log.info(s"$msg")
}
case msg @ GenericObjectStateMsg(object_guid, unk1) =>
@ -5381,6 +5383,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
_, //projectile_type,
thrown_projectile_vel
) =>
log.info(s"$msg")
HandleWeaponFire(weapon_guid, projectile_guid, shot_origin, thrown_projectile_vel.flatten)
case WeaponLazeTargetPositionMessage(_, _, _) => ;
@ -7535,7 +7538,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
/**
* Queue a proximity-base service.
* Queue a proximity-based service.
* @param terminal the proximity-based unit
* @param target the entity that is being considered for terminal operation
*/
@ -7547,7 +7550,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case _: Player =>
terminal.Actor ! CommonMessages.Use(player, Some(target))
case _: Vehicle =>
terminal.Actor ! CommonMessages.Use(player, Some((target, continent.VehicleEvents)))
terminal.Actor ! CommonMessages.Use(player, Some(target))
case _ =>
log.error(
s"StartUsingProximityUnit: ${player.Name}, this ${terminal.Definition.Name} can not deal with target $target"
@ -7562,38 +7565,30 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
/**
* Determine which functionality to pursue by a generic proximity-functional unit given the target for its activity.
* @see `VehicleService:receive, ProximityUnit.Action`
* Stop using a proximity-base service.
* If the suggested terminal detects our player or our player's vehicle as a valid target for its effect,
* inform it that we wish it stop affecting the discovered target(s).
* @param terminal the proximity-based unit
* @param target the object being affected by the unit
*/
def SelectProximityUnitBehavior(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = {
target match {
case o: Player =>
HealthAndArmorTerminal(terminal, o)
case _ => ;
def StopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
FindProximityUnitTargetsInScope(terminal).foreach { target =>
LocalStopUsingProximityUnit(terminal, target)
terminal.Actor ! CommonMessages.Unuse(player, Some(target))
}
}
/**
* Stop using a proximity-base service.
* Callback to handle flags specific to `SessionActor`.
* Special note is warranted when determining the identity of the proximity terminal.
* Medical terminals of both varieties can be cancelled by movement.
* Other sorts of proximity-based units are put on a timer.
* @param terminal the proximity-based unit
*/
def StopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
def LocalStopUsingProximityUnit(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = {
val term_guid = terminal.GUID
val targets = FindProximityUnitTargetsInScope(terminal)
if (targets.nonEmpty) {
if (usingMedicalTerminal.contains(term_guid)) {
usingMedicalTerminal = None
}
targets.foreach(target => terminal.Actor ! CommonMessages.Unuse(player, Some(target)))
} else {
log.warn(
s"StopUsingProximityUnit: ${player.Name} could not find valid targets for proximity unit ${terminal.Definition.Name}@${term_guid.guid}"
)
if (usingMedicalTerminal.contains(term_guid)) {
usingMedicalTerminal = None
}
}
@ -7623,72 +7618,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
}
/**
* When standing on the platform of a(n advanced) medical terminal,
* resotre the player's health and armor points (when they need their health and armor points restored).
* If the player is both fully healed and fully repaired, stop using the terminal.
* @param unit the medical terminal
* @param target the player being healed
*/
def HealthAndArmorTerminal(unit: Terminal with ProximityUnit, target: Player): Unit = {
val medDef = unit.Definition.asInstanceOf[MedicalTerminalDefinition]
val healAmount = medDef.HealAmount
val healthFull: Boolean = if (healAmount != 0 && target.Health < target.MaxHealth) {
target.History(HealFromTerm(PlayerSource(target), healAmount, 0, medDef))
HealAction(target, healAmount)
} else {
true
}
val repairAmount = medDef.ArmorAmount
val armorFull: Boolean = if (repairAmount != 0 && target.Armor < target.MaxArmor) {
target.History(HealFromTerm(PlayerSource(target), 0, repairAmount, medDef))
ArmorRepairAction(target, repairAmount)
} else {
true
}
if (healthFull && armorFull) {
StopUsingProximityUnit(unit)
}
}
/**
* Restore, at most, a specific amount of health points on a player.
* Send messages to connected client and to events system.
* @param tplayer the player
* @param healValue the amount to heal;
* 10 by default
* @return whether the player can be repaired for any more health points
*/
def HealAction(tplayer: Player, healValue: Int = 10): Boolean = {
val player_guid = tplayer.GUID
tplayer.Health = tplayer.Health + healValue
sendResponse(PlanetsideAttributeMessage(player_guid, 0, tplayer.Health))
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.PlanetsideAttribute(player_guid, 0, tplayer.Health)
)
tplayer.Health == tplayer.MaxHealth
}
/**
* Restore, at most, a specific amount of personal armor points on a player.
* Send messages to connected client and to events system.
* @param tplayer the player
* @param repairValue the amount to repair;
* 10 by default
* @return whether the player can be repaired for any more armor points
*/
def ArmorRepairAction(tplayer: Player, repairValue: Int = 10): Boolean = {
val player_guid = tplayer.GUID
tplayer.Armor = tplayer.Armor + repairValue
sendResponse(PlanetsideAttributeMessage(player_guid, 4, tplayer.Armor))
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.PlanetsideAttribute(player_guid, 4, tplayer.Armor)
)
tplayer.Armor == tplayer.MaxArmor
}
/**
* This function is applied to vehicles that are leaving a cargo vehicle's cargo hold to auto reverse them out
* Lock all applicable controls of the current vehicle
@ -9353,40 +9282,110 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
hitPos: Vector3
): List[(PlanetSideGameObject with FactionAffinity with Vitality, Projectile, Vector3, Vector3)] = {
GlobalDefinitions.getDamageProxy(projectile, hitPos) match {
case Some(proxy) if proxy.profile.ExistsOnRemoteClients =>
proxy.Position = hitPos
continent.Projectile ! ZoneProjectile.Add(player.GUID, proxy)
case Nil =>
Nil
case Some(proxy)
if proxy.tool_def == GlobalDefinitions.maelstrom =>
//server-side maelstrom grenade target selection
val radius = proxy.profile.LashRadius * proxy.profile.LashRadius
val targets = continent.blockMap
.sector(hitPos, proxy.profile.LashRadius)
.livePlayerList
.filter { target =>
Vector3.DistanceSquared(target.Position, hitPos) <= radius
}
//chainlash is separated from the actual damage application for convenience
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.SendResponse(
PlanetSideGUID(0),
ChainLashMessage(
hitPos,
projectile.profile.ObjectId,
targets.map { _.GUID }
case list =>
HandleDamageProxySetupLittleBuddy(list, hitPos)
list.flatMap { proxy =>
if (proxy.profile.ExistsOnRemoteClients) {
proxy.Position = hitPos
continent.Projectile ! ZoneProjectile.Add(player.GUID, proxy)
Nil
} else if (proxy.tool_def == GlobalDefinitions.maelstrom) {
//server-side maelstrom grenade target selection
val radius = proxy.profile.LashRadius * proxy.profile.LashRadius
val targets = continent.blockMap
.sector(hitPos, proxy.profile.LashRadius)
.livePlayerList
.filter { target =>
Vector3.DistanceSquared(target.Position, hitPos) <= radius
}
//chainlash is separated from the actual damage application for convenience
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.SendResponse(
PlanetSideGUID(0),
ChainLashMessage(
hitPos,
projectile.profile.ObjectId,
targets.map { _.GUID }
)
)
)
)
)
targets.map { target =>
CheckForHitPositionDiscrepancy(pguid, hitPos, target)
(target, proxy, hitPos, target.Position)
targets.map { target =>
CheckForHitPositionDiscrepancy(pguid, hitPos, target)
(target, proxy, hitPos, target.Position)
}
} else {
Nil
}
}
}
}
case _ =>
Nil
def HandleDamageProxySetupLittleBuddy(listOfProjectiles: List[Projectile], detonationPosition: Vector3): Boolean = {
val listOfLittleBuddies: List[Projectile] = listOfProjectiles.filter { _.tool_def == GlobalDefinitions.oicw }
val size: Int = listOfLittleBuddies.size
if (size > 0) {
val desiredDownwardsProjectiles: Int = 2
val firstHalf: Int = math.min(size, desiredDownwardsProjectiles) //number that fly straight down
val secondHalf: Int = math.max(size - firstHalf, 0) //number that are flared out
val z: Float = player.Orientation.z //player's standing direction
val north: Vector3 = Vector3(0,1,0) //map North
val speed: Float = 144f //speed (packet discovered)
val dist: Float = 25 //distance (client defined)
val downwardsAngle: Float = -85f
val flaredAngle: Float = -70f
//angle of separation for downwards, degrees from vertical for flared out
val (smallStep, smallAngle): (Float, Float) = if (firstHalf > 1) {
(360f / firstHalf, downwardsAngle)
} else {
(0f, 0f)
}
val (largeStep, largeAngle): (Float, Float) = if (secondHalf > 1) {
(360f / secondHalf, flaredAngle)
} else {
(0f, 0f)
}
val smallRotOffset: Float = z + 90f
val largeRotOffset: Float = z + math.random().toFloat * 45f
val verticalCorrection = Vector3.z(dist - dist * math.sin(math.toRadians(90 - smallAngle + largeAngle)).toFloat)
//downwards projectiles
var i: Int = 0
listOfLittleBuddies.take(firstHalf).foreach { proxy =>
val facing = (smallRotOffset + smallStep * i.toFloat) % 360
val dir = north.Rx(smallAngle).Rz(facing)
proxy.Position = detonationPosition + dir.xy + verticalCorrection
proxy.Velocity = dir * speed
proxy.Orientation = Vector3(0, (360f + smallAngle) % 360, facing)
HandleDamageProxyLittleBuddyExplosion(proxy, dir, dist)
i += 1
}
//flared out projectiles
i = 0
listOfLittleBuddies.drop(firstHalf).foreach { proxy =>
val facing = (largeRotOffset + largeStep * i.toFloat) % 360
val dir = north.Rx(largeAngle).Rz(facing)
proxy.Position = detonationPosition + dir
proxy.Velocity = dir * speed
proxy.Orientation = Vector3(0, (360f + largeAngle) % 360, facing)
HandleDamageProxyLittleBuddyExplosion(proxy, dir, dist)
i += 1
}
true
} else {
false
}
}
def HandleDamageProxyLittleBuddyExplosion(proxy: Projectile, orientation: Vector3, distance: Float): Unit = {
//explosion
val obj = DummyExplodingEntity(proxy)
obj.Position = obj.Position + orientation * distance
context.system.scheduler.scheduleOnce(500.milliseconds) {
val c = continent
val o = obj
Zone.serverSideDamage(c, o, Zone.explosionDamage(None, o.Position))
}
}

View file

@ -0,0 +1,64 @@
// Copyright (c) 2022 PSForever
package net.psforever.objects
import net.psforever.objects.definition.{ObjectDefinition, ProjectileDefinition}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
import net.psforever.objects.vital.{Vitality, VitalityDefinition}
import net.psforever.types.{PlanetSideEmpire, Vector3}
class DummyExplodingEntity(
private val obj: PlanetSideGameObject,
private val faction: PlanetSideEmpire.Value
)
extends PlanetSideGameObject
with FactionAffinity
with Vitality {
override def GUID = obj.GUID
override def Position: Vector3 = {
if (super.Position == Vector3.Zero) {
obj.Position
} else {
super.Position
}
}
override def Orientation: Vector3 = {
if (super.Orientation == Vector3.Zero) {
obj.Orientation
} else {
super.Orientation
}
}
override def Velocity : Option[Vector3] = {
super.Velocity.orElse(obj.Velocity)
}
def Faction: PlanetSideEmpire.Value = faction
def DamageModel: DamageAndResistance = DummyExplodingEntity.DefaultDamageResistanceModel
def Definition: ObjectDefinition with VitalityDefinition = {
new DefinitionWrappedInVitality(obj.Definition)
}
}
private class DefinitionWrappedInVitality(definition: ObjectDefinition)
extends ObjectDefinition(definition.ObjectId)
with VitalityDefinition {
innateDamage = definition match {
case v: VitalityDefinition if v.innateDamage.nonEmpty => v.innateDamage.get
case p: ProjectileDefinition => p
case _ => GlobalDefinitions.no_projectile
}
DefaultHealth = 1 //just cuz
}
object DummyExplodingEntity {
final val DefaultDamageResistanceModel = new DamageResistanceModel { }
def apply(obj: PlanetSideGameObject): DummyExplodingEntity = new DummyExplodingEntity(obj, PlanetSideEmpire.NEUTRAL)
}

View file

@ -28,7 +28,7 @@ import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, Turr
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, UtilityType, VehicleSubsystemEntry}
import net.psforever.objects.vital.base.DamageType
import net.psforever.objects.vital.damage._
import net.psforever.objects.vital.etc.{ArmorSiphonMaxDistanceCutoff, ExplodingRadialDegrade, InfantryAggravatedRadiation, InfantryAggravatedRadiationBurn}
import net.psforever.objects.vital.etc.{ShieldAgainstRadiation => _, _}
import net.psforever.objects.vital.projectile._
import net.psforever.objects.vital.prop.DamageWithPosition
import net.psforever.objects.vital._
@ -147,299 +147,299 @@ object GlobalDefinitions {
*/
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.Types.bullet_105mm_projectile)
val bullet_12mm_projectile = ProjectileDefinition(Projectiles.bullet_12mm_projectile)
val bullet_12mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_12mm_projectile)
val bullet_12mm_projectileb = ProjectileDefinition(Projectiles.bullet_12mm_projectileb)
val bullet_12mm_projectileb = ProjectileDefinition(Projectiles.Types.bullet_12mm_projectileb)
val bullet_150mm_projectile = ProjectileDefinition(Projectiles.bullet_150mm_projectile)
val bullet_150mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_150mm_projectile)
val bullet_15mm_apc_projectile = ProjectileDefinition(Projectiles.bullet_15mm_apc_projectile)
val bullet_15mm_apc_projectile = ProjectileDefinition(Projectiles.Types.bullet_15mm_apc_projectile)
val bullet_15mm_projectile = ProjectileDefinition(Projectiles.bullet_15mm_projectile)
val bullet_15mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_15mm_projectile)
val bullet_20mm_apc_projectile = ProjectileDefinition(Projectiles.bullet_20mm_apc_projectile)
val bullet_20mm_apc_projectile = ProjectileDefinition(Projectiles.Types.bullet_20mm_apc_projectile)
val bullet_20mm_projectile = ProjectileDefinition(Projectiles.bullet_20mm_projectile)
val bullet_20mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_20mm_projectile)
val bullet_25mm_projectile = ProjectileDefinition(Projectiles.bullet_25mm_projectile)
val bullet_25mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_25mm_projectile)
val bullet_35mm_projectile = ProjectileDefinition(Projectiles.bullet_35mm_projectile)
val bullet_35mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_35mm_projectile)
val bullet_75mm_apc_projectile = ProjectileDefinition(Projectiles.bullet_75mm_apc_projectile)
val bullet_75mm_apc_projectile = ProjectileDefinition(Projectiles.Types.bullet_75mm_apc_projectile)
val bullet_75mm_projectile = ProjectileDefinition(Projectiles.bullet_75mm_projectile)
val bullet_75mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_75mm_projectile)
val bullet_9mm_AP_projectile = ProjectileDefinition(Projectiles.bullet_9mm_AP_projectile)
val bullet_9mm_AP_projectile = ProjectileDefinition(Projectiles.Types.bullet_9mm_AP_projectile)
val bullet_9mm_projectile = ProjectileDefinition(Projectiles.bullet_9mm_projectile)
val bullet_9mm_projectile = ProjectileDefinition(Projectiles.Types.bullet_9mm_projectile)
val anniversary_projectilea = ProjectileDefinition(Projectiles.anniversary_projectilea)
val anniversary_projectilea = ProjectileDefinition(Projectiles.Types.anniversary_projectilea)
val anniversary_projectileb = ProjectileDefinition(Projectiles.anniversary_projectileb)
val anniversary_projectileb = ProjectileDefinition(Projectiles.Types.anniversary_projectileb)
val aphelion_immolation_cannon_projectile = ProjectileDefinition(Projectiles.aphelion_immolation_cannon_projectile)
val aphelion_immolation_cannon_projectile = ProjectileDefinition(Projectiles.Types.aphelion_immolation_cannon_projectile)
val aphelion_laser_projectile = ProjectileDefinition(Projectiles.aphelion_laser_projectile)
val aphelion_laser_projectile = ProjectileDefinition(Projectiles.Types.aphelion_laser_projectile)
val aphelion_plasma_cloud = ProjectileDefinition(Projectiles.aphelion_plasma_cloud)
val aphelion_plasma_cloud = ProjectileDefinition(Projectiles.Types.aphelion_plasma_cloud)
val aphelion_plasma_rocket_projectile = ProjectileDefinition(Projectiles.aphelion_plasma_rocket_projectile)
val aphelion_plasma_rocket_projectile = ProjectileDefinition(Projectiles.Types.aphelion_plasma_rocket_projectile)
val aphelion_ppa_projectile = ProjectileDefinition(Projectiles.aphelion_ppa_projectile)
val aphelion_ppa_projectile = ProjectileDefinition(Projectiles.Types.aphelion_ppa_projectile)
val aphelion_starfire_projectile = ProjectileDefinition(Projectiles.aphelion_starfire_projectile)
val aphelion_starfire_projectile = ProjectileDefinition(Projectiles.Types.aphelion_starfire_projectile)
val bolt_projectile = ProjectileDefinition(Projectiles.bolt_projectile)
val bolt_projectile = ProjectileDefinition(Projectiles.Types.bolt_projectile)
val burster_projectile = ProjectileDefinition(Projectiles.burster_projectile)
val burster_projectile = ProjectileDefinition(Projectiles.Types.burster_projectile)
val chainblade_projectile = ProjectileDefinition(Projectiles.chainblade_projectile)
val chainblade_projectile = ProjectileDefinition(Projectiles.Types.chainblade_projectile)
val colossus_100mm_projectile = ProjectileDefinition(Projectiles.colossus_100mm_projectile)
val colossus_100mm_projectile = ProjectileDefinition(Projectiles.Types.colossus_100mm_projectile)
val colossus_burster_projectile = ProjectileDefinition(Projectiles.colossus_burster_projectile)
val colossus_burster_projectile = ProjectileDefinition(Projectiles.Types.colossus_burster_projectile)
val colossus_chaingun_projectile = ProjectileDefinition(Projectiles.colossus_chaingun_projectile)
val colossus_chaingun_projectile = ProjectileDefinition(Projectiles.Types.colossus_chaingun_projectile)
val colossus_cluster_bomb_projectile = ProjectileDefinition(Projectiles.colossus_cluster_bomb_projectile)
val colossus_cluster_bomb_projectile = ProjectileDefinition(Projectiles.Types.colossus_cluster_bomb_projectile)
val colossus_tank_cannon_projectile = ProjectileDefinition(Projectiles.colossus_tank_cannon_projectile)
val colossus_tank_cannon_projectile = ProjectileDefinition(Projectiles.Types.colossus_tank_cannon_projectile)
val comet_projectile = ProjectileDefinition(Projectiles.comet_projectile)
val comet_projectile = ProjectileDefinition(Projectiles.Types.comet_projectile)
val dualcycler_projectile = ProjectileDefinition(Projectiles.dualcycler_projectile)
val dualcycler_projectile = ProjectileDefinition(Projectiles.Types.dualcycler_projectile)
val dynomite_projectile = ProjectileDefinition(Projectiles.dynomite_projectile)
val dynomite_projectile = ProjectileDefinition(Projectiles.Types.dynomite_projectile)
val energy_cell_projectile = ProjectileDefinition(Projectiles.energy_cell_projectile)
val energy_cell_projectile = ProjectileDefinition(Projectiles.Types.energy_cell_projectile)
val energy_gun_nc_projectile = ProjectileDefinition(Projectiles.energy_gun_nc_projectile)
val energy_gun_nc_projectile = ProjectileDefinition(Projectiles.Types.energy_gun_nc_projectile)
val energy_gun_tr_projectile = ProjectileDefinition(Projectiles.energy_gun_tr_projectile)
val energy_gun_tr_projectile = ProjectileDefinition(Projectiles.Types.energy_gun_tr_projectile)
val energy_gun_vs_projectile = ProjectileDefinition(Projectiles.energy_gun_vs_projectile)
val energy_gun_vs_projectile = ProjectileDefinition(Projectiles.Types.energy_gun_vs_projectile)
val enhanced_energy_cell_projectile = ProjectileDefinition(Projectiles.enhanced_energy_cell_projectile)
val enhanced_energy_cell_projectile = ProjectileDefinition(Projectiles.Types.enhanced_energy_cell_projectile)
val enhanced_quasar_projectile = ProjectileDefinition(Projectiles.enhanced_quasar_projectile)
val enhanced_quasar_projectile = ProjectileDefinition(Projectiles.Types.enhanced_quasar_projectile)
val falcon_projectile = ProjectileDefinition(Projectiles.falcon_projectile)
val falcon_projectile = ProjectileDefinition(Projectiles.Types.falcon_projectile)
val firebird_missile_projectile = ProjectileDefinition(Projectiles.firebird_missile_projectile)
val firebird_missile_projectile = ProjectileDefinition(Projectiles.Types.firebird_missile_projectile)
val flail_projectile = ProjectileDefinition(Projectiles.flail_projectile)
val flail_projectile = ProjectileDefinition(Projectiles.Types.flail_projectile)
val flamethrower_fire_cloud = ProjectileDefinition(Projectiles.flamethrower_fire_cloud)
val flamethrower_fire_cloud = ProjectileDefinition(Projectiles.Types.flamethrower_fire_cloud)
val flamethrower_fireball = ProjectileDefinition(Projectiles.flamethrower_fireball)
val flamethrower_fireball = ProjectileDefinition(Projectiles.Types.flamethrower_fireball)
val flamethrower_projectile = ProjectileDefinition(Projectiles.flamethrower_projectile)
val flamethrower_projectile = ProjectileDefinition(Projectiles.Types.flamethrower_projectile)
val flux_cannon_apc_projectile = ProjectileDefinition(Projectiles.flux_cannon_apc_projectile)
val flux_cannon_apc_projectile = ProjectileDefinition(Projectiles.Types.flux_cannon_apc_projectile)
val flux_cannon_thresher_projectile = ProjectileDefinition(Projectiles.flux_cannon_thresher_projectile)
val flux_cannon_thresher_projectile = ProjectileDefinition(Projectiles.Types.flux_cannon_thresher_projectile)
val fluxpod_projectile = ProjectileDefinition(Projectiles.fluxpod_projectile)
val fluxpod_projectile = ProjectileDefinition(Projectiles.Types.fluxpod_projectile)
val forceblade_projectile = ProjectileDefinition(Projectiles.forceblade_projectile)
val forceblade_projectile = ProjectileDefinition(Projectiles.Types.forceblade_projectile)
val frag_cartridge_projectile = ProjectileDefinition(Projectiles.frag_cartridge_projectile)
val frag_cartridge_projectile = ProjectileDefinition(Projectiles.Types.frag_cartridge_projectile)
val frag_cartridge_projectile_b = ProjectileDefinition(Projectiles.frag_cartridge_projectile_b)
val frag_cartridge_projectile_b = ProjectileDefinition(Projectiles.Types.frag_cartridge_projectile_b)
val frag_grenade_projectile = ProjectileDefinition(Projectiles.frag_grenade_projectile)
val frag_grenade_projectile = ProjectileDefinition(Projectiles.Types.frag_grenade_projectile)
val frag_grenade_projectile_enh = ProjectileDefinition(Projectiles.frag_grenade_projectile_enh)
val frag_grenade_projectile_enh = ProjectileDefinition(Projectiles.Types.frag_grenade_projectile_enh)
val galaxy_gunship_gun_projectile = ProjectileDefinition(Projectiles.galaxy_gunship_gun_projectile)
val galaxy_gunship_gun_projectile = ProjectileDefinition(Projectiles.Types.galaxy_gunship_gun_projectile)
val gauss_cannon_projectile = ProjectileDefinition(Projectiles.gauss_cannon_projectile)
val gauss_cannon_projectile = ProjectileDefinition(Projectiles.Types.gauss_cannon_projectile)
val grenade_projectile = ProjectileDefinition(Projectiles.grenade_projectile)
val grenade_projectile = ProjectileDefinition(Projectiles.Types.grenade_projectile)
val heavy_grenade_projectile = ProjectileDefinition(Projectiles.heavy_grenade_projectile)
val heavy_grenade_projectile = ProjectileDefinition(Projectiles.Types.heavy_grenade_projectile)
val heavy_rail_beam_projectile = ProjectileDefinition(Projectiles.heavy_rail_beam_projectile)
val heavy_rail_beam_projectile = ProjectileDefinition(Projectiles.Types.heavy_rail_beam_projectile)
val heavy_sniper_projectile = ProjectileDefinition(Projectiles.heavy_sniper_projectile)
val heavy_sniper_projectile = ProjectileDefinition(Projectiles.Types.heavy_sniper_projectile)
val hellfire_projectile = ProjectileDefinition(Projectiles.hellfire_projectile)
val hellfire_projectile = ProjectileDefinition(Projectiles.Types.hellfire_projectile)
val hunter_seeker_missile_dumbfire = ProjectileDefinition(Projectiles.hunter_seeker_missile_dumbfire)
val hunter_seeker_missile_dumbfire = ProjectileDefinition(Projectiles.Types.hunter_seeker_missile_dumbfire)
val hunter_seeker_missile_projectile = ProjectileDefinition(Projectiles.hunter_seeker_missile_projectile)
val hunter_seeker_missile_projectile = ProjectileDefinition(Projectiles.Types.hunter_seeker_missile_projectile)
val jammer_cartridge_projectile = ProjectileDefinition(Projectiles.jammer_cartridge_projectile)
val jammer_cartridge_projectile = ProjectileDefinition(Projectiles.Types.jammer_cartridge_projectile)
val jammer_cartridge_projectile_b = ProjectileDefinition(Projectiles.jammer_cartridge_projectile_b)
val jammer_cartridge_projectile_b = ProjectileDefinition(Projectiles.Types.jammer_cartridge_projectile_b)
val jammer_grenade_projectile = ProjectileDefinition(Projectiles.jammer_grenade_projectile)
val jammer_grenade_projectile = ProjectileDefinition(Projectiles.Types.jammer_grenade_projectile)
val jammer_grenade_projectile_enh = ProjectileDefinition(Projectiles.jammer_grenade_projectile_enh)
val jammer_grenade_projectile_enh = ProjectileDefinition(Projectiles.Types.jammer_grenade_projectile_enh)
val katana_projectile = ProjectileDefinition(Projectiles.katana_projectile)
val katana_projectile = ProjectileDefinition(Projectiles.Types.katana_projectile)
val katana_projectileb = ProjectileDefinition(Projectiles.katana_projectileb)
val katana_projectileb = ProjectileDefinition(Projectiles.Types.katana_projectileb)
val lancer_projectile = ProjectileDefinition(Projectiles.lancer_projectile)
val lancer_projectile = ProjectileDefinition(Projectiles.Types.lancer_projectile)
val lasher_projectile = ProjectileDefinition(Projectiles.lasher_projectile)
val lasher_projectile = ProjectileDefinition(Projectiles.Types.lasher_projectile)
val lasher_projectile_ap = ProjectileDefinition(Projectiles.lasher_projectile_ap)
val lasher_projectile_ap = ProjectileDefinition(Projectiles.Types.lasher_projectile_ap)
val liberator_bomb_cluster_bomblet_projectile = ProjectileDefinition(
Projectiles.liberator_bomb_cluster_bomblet_projectile
Projectiles.Types.liberator_bomb_cluster_bomblet_projectile
)
val liberator_bomb_cluster_projectile = ProjectileDefinition(Projectiles.liberator_bomb_cluster_projectile)
val liberator_bomb_cluster_projectile = ProjectileDefinition(Projectiles.Types.liberator_bomb_cluster_projectile)
val liberator_bomb_projectile = ProjectileDefinition(Projectiles.liberator_bomb_projectile)
val liberator_bomb_projectile = ProjectileDefinition(Projectiles.Types.liberator_bomb_projectile)
val maelstrom_grenade_damager = ProjectileDefinition(Projectiles.maelstrom_grenade_damager)
val maelstrom_grenade_damager = ProjectileDefinition(Projectiles.Types.maelstrom_grenade_damager)
val maelstrom_grenade_projectile = ProjectileDefinition(Projectiles.maelstrom_grenade_projectile)
val maelstrom_grenade_projectile = ProjectileDefinition(Projectiles.Types.maelstrom_grenade_projectile)
val maelstrom_grenade_projectile_contact = ProjectileDefinition(Projectiles.maelstrom_grenade_projectile_contact)
val maelstrom_grenade_projectile_contact = ProjectileDefinition(Projectiles.Types.maelstrom_grenade_projectile_contact)
val maelstrom_stream_projectile = ProjectileDefinition(Projectiles.maelstrom_stream_projectile)
val maelstrom_stream_projectile = ProjectileDefinition(Projectiles.Types.maelstrom_stream_projectile)
val magcutter_projectile = ProjectileDefinition(Projectiles.magcutter_projectile)
val magcutter_projectile = ProjectileDefinition(Projectiles.Types.magcutter_projectile)
val melee_ammo_projectile = ProjectileDefinition(Projectiles.melee_ammo_projectile)
val melee_ammo_projectile = ProjectileDefinition(Projectiles.Types.melee_ammo_projectile)
val meteor_common = ProjectileDefinition(Projectiles.meteor_common)
val meteor_common = ProjectileDefinition(Projectiles.Types.meteor_common)
val meteor_projectile_b_large = ProjectileDefinition(Projectiles.meteor_projectile_b_large)
val meteor_projectile_b_large = ProjectileDefinition(Projectiles.Types.meteor_projectile_b_large)
val meteor_projectile_b_medium = ProjectileDefinition(Projectiles.meteor_projectile_b_medium)
val meteor_projectile_b_medium = ProjectileDefinition(Projectiles.Types.meteor_projectile_b_medium)
val meteor_projectile_b_small = ProjectileDefinition(Projectiles.meteor_projectile_b_small)
val meteor_projectile_b_small = ProjectileDefinition(Projectiles.Types.meteor_projectile_b_small)
val meteor_projectile_large = ProjectileDefinition(Projectiles.meteor_projectile_large)
val meteor_projectile_large = ProjectileDefinition(Projectiles.Types.meteor_projectile_large)
val meteor_projectile_medium = ProjectileDefinition(Projectiles.meteor_projectile_medium)
val meteor_projectile_medium = ProjectileDefinition(Projectiles.Types.meteor_projectile_medium)
val meteor_projectile_small = ProjectileDefinition(Projectiles.meteor_projectile_small)
val meteor_projectile_small = ProjectileDefinition(Projectiles.Types.meteor_projectile_small)
val mine_projectile = ProjectileDefinition(Projectiles.mine_projectile)
val mine_projectile = ProjectileDefinition(Projectiles.Types.mine_projectile)
val mine_sweeper_projectile = ProjectileDefinition(Projectiles.mine_sweeper_projectile)
val mine_sweeper_projectile = ProjectileDefinition(Projectiles.Types.mine_sweeper_projectile)
val mine_sweeper_projectile_enh = ProjectileDefinition(Projectiles.mine_sweeper_projectile_enh)
val mine_sweeper_projectile_enh = ProjectileDefinition(Projectiles.Types.mine_sweeper_projectile_enh)
val oicw_projectile = ProjectileDefinition(Projectiles.oicw_projectile)
val oicw_projectile = ProjectileDefinition(Projectiles.Types.oicw_projectile)
val oicw_little_buddy = ProjectileDefinition(Projectiles.oicw_little_buddy)
val oicw_little_buddy = ProjectileDefinition(Projectiles.Types.oicw_little_buddy)
val pellet_gun_projectile = ProjectileDefinition(Projectiles.pellet_gun_projectile)
val pellet_gun_projectile = ProjectileDefinition(Projectiles.Types.pellet_gun_projectile)
val peregrine_dual_machine_gun_projectile = ProjectileDefinition(Projectiles.peregrine_dual_machine_gun_projectile)
val peregrine_dual_machine_gun_projectile = ProjectileDefinition(Projectiles.Types.peregrine_dual_machine_gun_projectile)
val peregrine_mechhammer_projectile = ProjectileDefinition(Projectiles.peregrine_mechhammer_projectile)
val peregrine_mechhammer_projectile = ProjectileDefinition(Projectiles.Types.peregrine_mechhammer_projectile)
val peregrine_particle_cannon_projectile = ProjectileDefinition(Projectiles.peregrine_particle_cannon_projectile)
val peregrine_particle_cannon_projectile = ProjectileDefinition(Projectiles.Types.peregrine_particle_cannon_projectile)
val peregrine_particle_cannon_radiation_cloud = ProjectileDefinition(Projectiles.peregrine_particle_cannon_radiation_cloud)
val peregrine_particle_cannon_radiation_cloud = ProjectileDefinition(Projectiles.Types.peregrine_particle_cannon_radiation_cloud)
val peregrine_rocket_pod_projectile = ProjectileDefinition(Projectiles.peregrine_rocket_pod_projectile)
val peregrine_rocket_pod_projectile = ProjectileDefinition(Projectiles.Types.peregrine_rocket_pod_projectile)
val peregrine_sparrow_projectile = ProjectileDefinition(Projectiles.peregrine_sparrow_projectile)
val peregrine_sparrow_projectile = ProjectileDefinition(Projectiles.Types.peregrine_sparrow_projectile)
val phalanx_av_projectile = ProjectileDefinition(Projectiles.phalanx_av_projectile)
val phalanx_av_projectile = ProjectileDefinition(Projectiles.Types.phalanx_av_projectile)
val phalanx_flak_projectile = ProjectileDefinition(Projectiles.phalanx_flak_projectile)
val phalanx_flak_projectile = ProjectileDefinition(Projectiles.Types.phalanx_flak_projectile)
val phalanx_projectile = ProjectileDefinition(Projectiles.phalanx_projectile)
val phalanx_projectile = ProjectileDefinition(Projectiles.Types.phalanx_projectile)
val phoenix_missile_guided_projectile = ProjectileDefinition(Projectiles.phoenix_missile_guided_projectile)
val phoenix_missile_guided_projectile = ProjectileDefinition(Projectiles.Types.phoenix_missile_guided_projectile)
val phoenix_missile_projectile = ProjectileDefinition(Projectiles.phoenix_missile_projectile)
val phoenix_missile_projectile = ProjectileDefinition(Projectiles.Types.phoenix_missile_projectile)
val plasma_cartridge_projectile = ProjectileDefinition(Projectiles.plasma_cartridge_projectile)
val plasma_cartridge_projectile = ProjectileDefinition(Projectiles.Types.plasma_cartridge_projectile)
val plasma_cartridge_projectile_b = ProjectileDefinition(Projectiles.plasma_cartridge_projectile_b)
val plasma_cartridge_projectile_b = ProjectileDefinition(Projectiles.Types.plasma_cartridge_projectile_b)
val plasma_grenade_projectile = ProjectileDefinition(Projectiles.plasma_grenade_projectile)
val plasma_grenade_projectile = ProjectileDefinition(Projectiles.Types.plasma_grenade_projectile)
val plasma_grenade_projectile_B = ProjectileDefinition(Projectiles.plasma_grenade_projectile_B)
val plasma_grenade_projectile_B = ProjectileDefinition(Projectiles.Types.plasma_grenade_projectile_B)
val pounder_projectile = ProjectileDefinition(Projectiles.pounder_projectile)
val pounder_projectile = ProjectileDefinition(Projectiles.Types.pounder_projectile)
val pounder_projectile_enh = ProjectileDefinition(Projectiles.pounder_projectile_enh)
val pounder_projectile_enh = ProjectileDefinition(Projectiles.Types.pounder_projectile_enh)
val ppa_projectile = ProjectileDefinition(Projectiles.ppa_projectile)
val ppa_projectile = ProjectileDefinition(Projectiles.Types.ppa_projectile)
val pulsar_ap_projectile = ProjectileDefinition(Projectiles.pulsar_ap_projectile)
val pulsar_ap_projectile = ProjectileDefinition(Projectiles.Types.pulsar_ap_projectile)
val pulsar_projectile = ProjectileDefinition(Projectiles.pulsar_projectile)
val pulsar_projectile = ProjectileDefinition(Projectiles.Types.pulsar_projectile)
val quasar_projectile = ProjectileDefinition(Projectiles.quasar_projectile)
val quasar_projectile = ProjectileDefinition(Projectiles.Types.quasar_projectile)
val radiator_cloud = ProjectileDefinition(Projectiles.radiator_cloud)
val radiator_cloud = ProjectileDefinition(Projectiles.Types.radiator_cloud)
val radiator_grenade_projectile = ProjectileDefinition(Projectiles.radiator_grenade_projectile)
val radiator_grenade_projectile = ProjectileDefinition(Projectiles.Types.radiator_grenade_projectile)
val radiator_sticky_projectile = ProjectileDefinition(Projectiles.radiator_sticky_projectile)
val radiator_sticky_projectile = ProjectileDefinition(Projectiles.Types.radiator_sticky_projectile)
val reaver_rocket_projectile = ProjectileDefinition(Projectiles.reaver_rocket_projectile)
val reaver_rocket_projectile = ProjectileDefinition(Projectiles.Types.reaver_rocket_projectile)
val rocket_projectile = ProjectileDefinition(Projectiles.rocket_projectile)
val rocket_projectile = ProjectileDefinition(Projectiles.Types.rocket_projectile)
val rocklet_flak_projectile = ProjectileDefinition(Projectiles.rocklet_flak_projectile)
val rocklet_flak_projectile = ProjectileDefinition(Projectiles.Types.rocklet_flak_projectile)
val rocklet_jammer_projectile = ProjectileDefinition(Projectiles.rocklet_jammer_projectile)
val rocklet_jammer_projectile = ProjectileDefinition(Projectiles.Types.rocklet_jammer_projectile)
val scattercannon_projectile = ProjectileDefinition(Projectiles.scattercannon_projectile)
val scattercannon_projectile = ProjectileDefinition(Projectiles.Types.scattercannon_projectile)
val scythe_projectile = ProjectileDefinition(Projectiles.scythe_projectile)
val scythe_projectile = ProjectileDefinition(Projectiles.Types.scythe_projectile)
val scythe_projectile_slave = ProjectileDefinition(Projectiles.scythe_projectile_slave)
val scythe_projectile_slave = ProjectileDefinition(Projectiles.Types.scythe_projectile_slave)
val shotgun_shell_AP_projectile = ProjectileDefinition(Projectiles.shotgun_shell_AP_projectile)
val shotgun_shell_AP_projectile = ProjectileDefinition(Projectiles.Types.shotgun_shell_AP_projectile)
val shotgun_shell_projectile = ProjectileDefinition(Projectiles.shotgun_shell_projectile)
val shotgun_shell_projectile = ProjectileDefinition(Projectiles.Types.shotgun_shell_projectile)
val six_shooter_projectile = ProjectileDefinition(Projectiles.six_shooter_projectile)
val six_shooter_projectile = ProjectileDefinition(Projectiles.Types.six_shooter_projectile)
val skyguard_flak_cannon_projectile = ProjectileDefinition(Projectiles.skyguard_flak_cannon_projectile)
val skyguard_flak_cannon_projectile = ProjectileDefinition(Projectiles.Types.skyguard_flak_cannon_projectile)
val sparrow_projectile = ProjectileDefinition(Projectiles.sparrow_projectile)
val sparrow_projectile = ProjectileDefinition(Projectiles.Types.sparrow_projectile)
val sparrow_secondary_projectile = ProjectileDefinition(Projectiles.sparrow_secondary_projectile)
val sparrow_secondary_projectile = ProjectileDefinition(Projectiles.Types.sparrow_secondary_projectile)
val spiker_projectile = ProjectileDefinition(Projectiles.spiker_projectile)
val spiker_projectile = ProjectileDefinition(Projectiles.Types.spiker_projectile)
val spitfire_aa_ammo_projectile = ProjectileDefinition(Projectiles.spitfire_aa_ammo_projectile)
val spitfire_aa_ammo_projectile = ProjectileDefinition(Projectiles.Types.spitfire_aa_ammo_projectile)
val spitfire_ammo_projectile = ProjectileDefinition(Projectiles.spitfire_ammo_projectile)
val spitfire_ammo_projectile = ProjectileDefinition(Projectiles.Types.spitfire_ammo_projectile)
val starfire_projectile = ProjectileDefinition(Projectiles.starfire_projectile)
val starfire_projectile = ProjectileDefinition(Projectiles.Types.starfire_projectile)
val striker_missile_projectile = ProjectileDefinition(Projectiles.striker_missile_projectile)
val striker_missile_projectile = ProjectileDefinition(Projectiles.Types.striker_missile_projectile)
val striker_missile_targeting_projectile = ProjectileDefinition(Projectiles.striker_missile_targeting_projectile)
val striker_missile_targeting_projectile = ProjectileDefinition(Projectiles.Types.striker_missile_targeting_projectile)
val trek_projectile = ProjectileDefinition(Projectiles.trek_projectile)
val trek_projectile = ProjectileDefinition(Projectiles.Types.trek_projectile)
val vanu_sentry_turret_projectile = ProjectileDefinition(Projectiles.vanu_sentry_turret_projectile)
val vanu_sentry_turret_projectile = ProjectileDefinition(Projectiles.Types.vanu_sentry_turret_projectile)
val vulture_bomb_projectile = ProjectileDefinition(Projectiles.vulture_bomb_projectile)
val vulture_bomb_projectile = ProjectileDefinition(Projectiles.Types.vulture_bomb_projectile)
val vulture_nose_bullet_projectile = ProjectileDefinition(Projectiles.vulture_nose_bullet_projectile)
val vulture_nose_bullet_projectile = ProjectileDefinition(Projectiles.Types.vulture_nose_bullet_projectile)
val vulture_tail_bullet_projectile = ProjectileDefinition(Projectiles.vulture_tail_bullet_projectile)
val vulture_tail_bullet_projectile = ProjectileDefinition(Projectiles.Types.vulture_tail_bullet_projectile)
val wasp_gun_projectile = ProjectileDefinition(Projectiles.wasp_gun_projectile)
val wasp_gun_projectile = ProjectileDefinition(Projectiles.Types.wasp_gun_projectile)
val wasp_rocket_projectile = ProjectileDefinition(Projectiles.wasp_rocket_projectile)
val wasp_rocket_projectile = ProjectileDefinition(Projectiles.Types.wasp_rocket_projectile)
val winchester_projectile = ProjectileDefinition(Projectiles.winchester_projectile)
val winchester_projectile = ProjectileDefinition(Projectiles.Types.winchester_projectile)
val armor_siphon_projectile = ProjectileDefinition(Projectiles.trek_projectile) //fake projectile for storing damage information
val armor_siphon_projectile = ProjectileDefinition(Projectiles.Types.trek_projectile) //fake projectile for storing damage information
val ntu_siphon_emp = ProjectileDefinition(Projectiles.ntu_siphon_emp)
val ntu_siphon_emp = ProjectileDefinition(Projectiles.Types.ntu_siphon_emp)
init_projectile()
/*
@ -1180,7 +1180,9 @@ object GlobalDefinitions {
val repair_silo = new MedicalTerminalDefinition(729)
val recharge_terminal = new MedicalTerminalDefinition(724)
val recharge_terminal = new WeaponRechargeTerminalDefinition(724)
val recharge_terminal_weapon_module = new WeaponRechargeTerminalDefinition(725)
val mb_pad_creation = new VehicleSpawnPadDefinition(525)
@ -1975,28 +1977,31 @@ object GlobalDefinitions {
}
/**
* Return a projectile that is the damage proxy of another projectile,
* Return projectiles that are the damage proxies of another projectile,
* if such a damage proxy is defined in the appropriate field by its unique object identifier.
* @see `ProjectileDefinition.DamageProxy`
* @param projectile the original projectile
* @return the damage proxy projectile definition, if that can be produced
* @return the damage proxy projectiles, if they can be produced
*/
def getDamageProxy(projectile: Projectile, hitPosition: Vector3): Option[Projectile] = {
projectile.Definition.DamageProxy match {
case Some(uoid) =>
def getDamageProxy(projectile: Projectile, hitPosition: Vector3): List[Projectile] = {
projectile
.Definition
.DamageProxy
.flatMap { uoid =>
((uoid: @switch) match {
case 96 => Some(aphelion_plasma_cloud)
case 301 => Some(projectile.profile) //'flamethrower_fire_cloud' can not be made into a packet
case 464 => Some(projectile.profile) //'maelstrom_grenade_damager' can not be made into a packet
case 601 => Some(oicw_little_buddy)
case 655 => Some(peregrine_particle_cannon_radiation_cloud)
case 717 => Some(radiator_cloud)
case _ => None
}) match {
case Some(proxy)
if proxy eq projectile.profile =>
Some(projectile)
List(projectile)
case Some(proxy) =>
Some(Projectile(
List(Projectile(
proxy,
projectile.tool_def,
projectile.fire_mode,
@ -2006,11 +2011,9 @@ object GlobalDefinitions {
Vector3.Zero
))
case None =>
None
Nil
}
case None =>
None
}
}
}
/**
@ -3638,12 +3641,16 @@ object GlobalDefinitions {
oicw_projectile.ProjectileDamageType = DamageType.Splash
oicw_projectile.InitialVelocity = 5
oicw_projectile.Lifespan = 6.1f
oicw_projectile.DamageProxy = List(601, 601, 601, 601, 601) //5 x oicw_little_buddy
oicw_projectile.registerAs = "rc-projectiles"
oicw_projectile.ExistsOnRemoteClients = true
oicw_projectile.RemoteClientData = (13107, 195)
oicw_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(oicw_projectile)
oicw_projectile.Modifiers = RadialDegrade
oicw_projectile.Modifiers = List(
//ExplodingRadialDegrade,
RadialDegrade
)
oicw_little_buddy.Name = "oicw_little_buddy"
oicw_little_buddy.Damage0 = 75
@ -3653,11 +3660,14 @@ object GlobalDefinitions {
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
oicw_little_buddy.registerAs = "rc-projectiles"
oicw_little_buddy.ExistsOnRemoteClients = true //does not use RemoteClientData
oicw_little_buddy.Packet = new LittleBuddyProjectileConverter
//add_property oicw_little_buddy multi_stage_spawn_server_side true ...
ProjectileDefinition.CalculateDerivedFields(oicw_little_buddy)
oicw_little_buddy.Modifiers = RadialDegrade
oicw_little_buddy.Modifiers = List(
ExplosionDamagesOnlyAbove
)
pellet_gun_projectile.Name = "pellet_gun_projectile"
// TODO for later, maybe : set_resource_parent pellet_gun_projectile game_objects shotgun_shell_projectile
@ -3943,16 +3953,29 @@ object GlobalDefinitions {
radiator_cloud.Damage0 = 2
radiator_cloud.DamageAtEdge = 1.0f
radiator_cloud.DamageRadius = 5f
radiator_cloud.DamageToHealthOnly = true
radiator_cloud.radiation_cloud = true
radiator_cloud.ProjectileDamageType = DamageType.Radiation
//custom aggravated information
radiator_cloud.ProjectileDamageTypeSecondary = DamageType.Aggravated
radiator_cloud.Aggravated = AggravatedDamage(
AggravatedInfo(DamageType.Splash, 1f, 80),
Aura.None,
AggravatedTiming(250, 2),
0f,
false,
List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
)
radiator_cloud.Lifespan = 10.0f
ProjectileDefinition.CalculateDerivedFields(radiator_cloud)
radiator_cloud.registerAs = "rc-projectiles"
radiator_cloud.ExistsOnRemoteClients = true
radiator_cloud.Packet = radCloudConverter
radiator_cloud.Geometry = GeometryForm.representProjectileBySphere()
//radiator_cloud.Geometry = GeometryForm.representProjectileBySphere()
radiator_cloud.Modifiers = List(
MaxDistanceCutoff,
InfantryAggravatedRadiation,
InfantryAggravatedRadiationBurn,
ShieldAgainstRadiation
)
@ -3961,6 +3984,7 @@ object GlobalDefinitions {
radiator_grenade_projectile.ProjectileDamageType = DamageType.Direct
radiator_grenade_projectile.InitialVelocity = 30
radiator_grenade_projectile.Lifespan = 3f
radiator_grenade_projectile.DamageProxy = 717 //radiator_cloud
ProjectileDefinition.CalculateDerivedFields(radiator_grenade_projectile)
radiator_sticky_projectile.Name = "radiator_sticky_projectile"
@ -3969,6 +3993,7 @@ object GlobalDefinitions {
radiator_sticky_projectile.ProjectileDamageType = DamageType.Direct
radiator_sticky_projectile.InitialVelocity = 30
radiator_sticky_projectile.Lifespan = 4f
radiator_sticky_projectile.DamageProxy = 717 //radiator_cloud
ProjectileDefinition.CalculateDerivedFields(radiator_sticky_projectile)
reaver_rocket_projectile.Name = "reaver_rocket_projectile"
@ -4430,7 +4455,7 @@ object GlobalDefinitions {
aphelion_plasma_cloud.registerAs = "rc-projectiles"
aphelion_plasma_cloud.ExistsOnRemoteClients = true
aphelion_plasma_cloud.Packet = radCloudConverter
aphelion_plasma_cloud.Geometry = GeometryForm.representProjectileBySphere()
//aphelion_plasma_cloud.Geometry = GeometryForm.representProjectileBySphere()
aphelion_plasma_cloud.Modifiers = List( //TODO placeholder values
MaxDistanceCutoff,
InfantryAggravatedRadiation,
@ -4632,7 +4657,7 @@ object GlobalDefinitions {
peregrine_particle_cannon_radiation_cloud.registerAs = "rc-projectiles"
peregrine_particle_cannon_radiation_cloud.ExistsOnRemoteClients = true
peregrine_particle_cannon_radiation_cloud.Packet = radCloudConverter
peregrine_particle_cannon_radiation_cloud.Geometry = GeometryForm.representProjectileBySphere()
//peregrine_particle_cannon_radiation_cloud.Geometry = GeometryForm.representProjectileBySphere()
peregrine_particle_cannon_radiation_cloud.Modifiers = List(
MaxDistanceCutoff,
ShieldAgainstRadiation
@ -9615,6 +9640,13 @@ object GlobalDefinitions {
recharge_terminal.Damageable = false
recharge_terminal.Repairable = false
recharge_terminal_weapon_module.Name = "recharge_terminal_weapon_module"
recharge_terminal_weapon_module.Interval = 1000
recharge_terminal_weapon_module.UseRadius = 300
recharge_terminal_weapon_module.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.AncientWeaponRecharge
recharge_terminal_weapon_module.Damageable = false
recharge_terminal_weapon_module.Repairable = false
mb_pad_creation.Name = "mb_pad_creation"
mb_pad_creation.Damageable = false
mb_pad_creation.Repairable = false

View file

@ -78,7 +78,7 @@ class Tool(private val toolDef: ToolDefinition)
})
}
def ProjectileType: Projectiles.Value = Projectile.ProjectileType
def ProjectileType: Projectiles.Types.Value = Projectile.ProjectileType
def Magazine: Int = AmmoSlot.Magazine

View file

@ -62,7 +62,7 @@ class InteractWithRadiationClouds(
RadiationReason(
ProjectileQuality.modifiers(projectile, DamageResolution.Radiation, t, t.Position, user),
t.DamageModel,
1f
0f
),
position
).calculate()

View file

@ -5,151 +5,153 @@ package net.psforever.objects.ballistics
* An `Enumeration` of all the projectile types in the game, paired with their object id as the `Value`.
*/
object Projectiles extends Enumeration {
final val no_projectile = Value(0)
object Types extends Enumeration {
final val no_projectile = Value(0)
final val bullet_105mm_projectile = Value(1)
final val bullet_12mm_projectile = Value(4)
final val bullet_12mm_projectileb = Value(5)
final val bullet_150mm_projectile = Value(7)
final val bullet_15mm_apc_projectile = Value(10)
final val bullet_15mm_projectile = Value(11)
final val bullet_20mm_apc_projectile = Value(17)
final val bullet_20mm_projectile = Value(18)
final val bullet_25mm_projectile = Value(20)
final val bullet_35mm_projectile = Value(22)
final val bullet_75mm_apc_projectile = Value(26)
final val bullet_75mm_projectile = Value(27)
final val bullet_9mm_AP_projectile = Value(30)
final val bullet_9mm_projectile = Value(31)
final val anniversary_projectilea = Value(58)
final val anniversary_projectileb = Value(59)
final val aphelion_immolation_cannon_projectile = Value(87)
final val aphelion_laser_projectile = Value(91)
final val aphelion_plasma_cloud = Value(96) //radiation cloud
final val aphelion_plasma_rocket_projectile = Value(99)
final val aphelion_ppa_projectile = Value(103)
final val aphelion_starfire_projectile = Value(108)
final val bolt_projectile = Value(147)
final val burster_projectile = Value(155)
final val chainblade_projectile = Value(176)
final val colossus_100mm_projectile = Value(181)
final val colossus_burster_projectile = Value(188)
final val colossus_chaingun_projectile = Value(193)
final val colossus_cluster_bomb_projectile = Value(197)
final val colossus_tank_cannon_projectile = Value(207)
final val comet_projectile = Value(210)
final val dualcycler_projectile = Value(266)
final val dynomite_projectile = Value(268)
final val energy_cell_projectile = Value(273)
final val energy_gun_nc_projectile = Value(277)
final val energy_gun_tr_projectile = Value(279)
final val energy_gun_vs_projectile = Value(281)
final val enhanced_energy_cell_projectile = Value(282)
final val enhanced_quasar_projectile = Value(283)
final val falcon_projectile = Value(286)
final val firebird_missile_projectile = Value(288)
final val flail_projectile = Value(296)
final val flamethrower_fire_cloud = Value(301)
final val flamethrower_fireball = Value(302)
final val flamethrower_projectile = Value(303)
final val flux_cannon_apc_projectile = Value(305)
final val flux_cannon_thresher_projectile = Value(308)
final val fluxpod_projectile = Value(311)
final val forceblade_projectile = Value(325)
final val frag_cartridge_projectile = Value(328)
final val frag_cartridge_projectile_b = Value(329)
final val frag_grenade_projectile = Value(332)
final val frag_grenade_projectile_enh = Value(333)
final val galaxy_gunship_gun_projectile = Value(341)
final val gauss_cannon_projectile = Value(348)
final val grenade_projectile = Value(372)
final val heavy_grenade_projectile = Value(392)
final val heavy_rail_beam_projectile = Value(395)
final val heavy_sniper_projectile = Value(397)
final val hellfire_projectile = Value(400)
final val hunter_seeker_missile_dumbfire = Value(404)
final val hunter_seeker_missile_projectile = Value(405)
final val jammer_cartridge_projectile = Value(414)
final val jammer_cartridge_projectile_b = Value(415)
final val jammer_grenade_projectile = Value(418)
final val jammer_grenade_projectile_enh = Value(419)
final val katana_projectile = Value(422)
final val katana_projectileb = Value(423)
final val lancer_projectile = Value(427)
final val lasher_projectile = Value(430)
final val lasher_projectile_ap = Value(431)
final val liberator_bomb_cluster_bomblet_projectile = Value(436)
final val liberator_bomb_cluster_projectile = Value(437)
final val liberator_bomb_projectile = Value(438)
final val maelstrom_grenade_damager = Value(464)
final val maelstrom_grenade_projectile = Value(465)
final val maelstrom_grenade_projectile_contact = Value(466)
final val maelstrom_stream_projectile = Value(467)
final val magcutter_projectile = Value(469)
final val melee_ammo_projectile = Value(541)
final val meteor_common = Value(543)
final val meteor_projectile_b_large = Value(544)
final val meteor_projectile_b_medium = Value(545)
final val meteor_projectile_b_small = Value(546)
final val meteor_projectile_large = Value(547)
final val meteor_projectile_medium = Value(548)
final val meteor_projectile_small = Value(549)
final val mine_projectile = Value(551)
final val mine_sweeper_projectile = Value(554)
final val mine_sweeper_projectile_enh = Value(555)
final val ntu_siphon_emp = Value(596)
final val oicw_little_buddy = Value(601)
final val oicw_projectile = Value(602)
final val pellet_gun_projectile = Value(631)
final val peregrine_dual_machine_gun_projectile = Value(639)
final val peregrine_mechhammer_projectile = Value(647)
final val peregrine_particle_cannon_projectile = Value(654)
final val peregrine_particle_cannon_radiation_cloud = Value(655) //radiation cloud
final val peregrine_rocket_pod_projectile = Value(657)
final val peregrine_sparrow_projectile = Value(661)
final val phalanx_av_projectile = Value(665)
final val phalanx_flak_projectile = Value(667)
final val phalanx_projectile = Value(669)
final val phoenix_missile_guided_projectile = Value(675)
final val phoenix_missile_projectile = Value(676)
final val plasma_cartridge_projectile = Value(678)
final val plasma_cartridge_projectile_b = Value(679)
final val plasma_grenade_projectile = Value(682)
final val plasma_grenade_projectile_B = Value(683)
final val pounder_projectile = Value(694)
final val pounder_projectile_enh = Value(695)
final val ppa_projectile = Value(696)
final val pulsar_ap_projectile = Value(702)
final val pulsar_projectile = Value(703)
final val quasar_projectile = Value(713)
final val radiator_cloud = Value(717) //radiation cloud
final val radiator_grenade_projectile = Value(718)
final val radiator_sticky_projectile = Value(719)
final val reaver_rocket_projectile = Value(723)
final val rocket_projectile = Value(735)
final val rocklet_flak_projectile = Value(738)
final val rocklet_jammer_projectile = Value(739)
final val scattercannon_projectile = Value(746)
final val scythe_projectile = Value(748)
final val scythe_projectile_slave = Value(749)
final val shotgun_shell_AP_projectile = Value(757)
final val shotgun_shell_projectile = Value(758)
final val six_shooter_projectile = Value(763)
final val skyguard_flak_cannon_projectile = Value(787)
final val sparrow_projectile = Value(792)
final val sparrow_secondary_projectile = Value(793)
final val spiker_projectile = Value(818)
final val spitfire_aa_ammo_projectile = Value(821)
final val spitfire_ammo_projectile = Value(824)
final val starfire_projectile = Value(831)
final val striker_missile_projectile = Value(840)
final val striker_missile_targeting_projectile = Value(841)
final val trek_projectile = Value(878)
final val vanu_sentry_turret_projectile = Value(944)
final val vulture_bomb_projectile = Value(988)
final val vulture_nose_bullet_projectile = Value(989)
final val vulture_tail_bullet_projectile = Value(991)
final val wasp_gun_projectile = Value(999)
final val wasp_rocket_projectile = Value(1001)
final val winchester_projectile = Value(1005)
final val bullet_105mm_projectile = Value(1)
final val bullet_12mm_projectile = Value(4)
final val bullet_12mm_projectileb = Value(5)
final val bullet_150mm_projectile = Value(7)
final val bullet_15mm_apc_projectile = Value(10)
final val bullet_15mm_projectile = Value(11)
final val bullet_20mm_apc_projectile = Value(17)
final val bullet_20mm_projectile = Value(18)
final val bullet_25mm_projectile = Value(20)
final val bullet_35mm_projectile = Value(22)
final val bullet_75mm_apc_projectile = Value(26)
final val bullet_75mm_projectile = Value(27)
final val bullet_9mm_AP_projectile = Value(30)
final val bullet_9mm_projectile = Value(31)
final val anniversary_projectilea = Value(58)
final val anniversary_projectileb = Value(59)
final val aphelion_immolation_cannon_projectile = Value(87)
final val aphelion_laser_projectile = Value(91)
final val aphelion_plasma_cloud = Value(96) //radiation cloud
final val aphelion_plasma_rocket_projectile = Value(99)
final val aphelion_ppa_projectile = Value(103)
final val aphelion_starfire_projectile = Value(108)
final val bolt_projectile = Value(147)
final val burster_projectile = Value(155)
final val chainblade_projectile = Value(176)
final val colossus_100mm_projectile = Value(181)
final val colossus_burster_projectile = Value(188)
final val colossus_chaingun_projectile = Value(193)
final val colossus_cluster_bomb_projectile = Value(197)
final val colossus_tank_cannon_projectile = Value(207)
final val comet_projectile = Value(210)
final val dualcycler_projectile = Value(266)
final val dynomite_projectile = Value(268)
final val energy_cell_projectile = Value(273)
final val energy_gun_nc_projectile = Value(277)
final val energy_gun_tr_projectile = Value(279)
final val energy_gun_vs_projectile = Value(281)
final val enhanced_energy_cell_projectile = Value(282)
final val enhanced_quasar_projectile = Value(283)
final val falcon_projectile = Value(286)
final val firebird_missile_projectile = Value(288)
final val flail_projectile = Value(296)
final val flamethrower_fire_cloud = Value(301)
final val flamethrower_fireball = Value(302)
final val flamethrower_projectile = Value(303)
final val flux_cannon_apc_projectile = Value(305)
final val flux_cannon_thresher_projectile = Value(308)
final val fluxpod_projectile = Value(311)
final val forceblade_projectile = Value(325)
final val frag_cartridge_projectile = Value(328)
final val frag_cartridge_projectile_b = Value(329)
final val frag_grenade_projectile = Value(332)
final val frag_grenade_projectile_enh = Value(333)
final val galaxy_gunship_gun_projectile = Value(341)
final val gauss_cannon_projectile = Value(348)
final val grenade_projectile = Value(372)
final val heavy_grenade_projectile = Value(392)
final val heavy_rail_beam_projectile = Value(395)
final val heavy_sniper_projectile = Value(397)
final val hellfire_projectile = Value(400)
final val hunter_seeker_missile_dumbfire = Value(404)
final val hunter_seeker_missile_projectile = Value(405)
final val jammer_cartridge_projectile = Value(414)
final val jammer_cartridge_projectile_b = Value(415)
final val jammer_grenade_projectile = Value(418)
final val jammer_grenade_projectile_enh = Value(419)
final val katana_projectile = Value(422)
final val katana_projectileb = Value(423)
final val lancer_projectile = Value(427)
final val lasher_projectile = Value(430)
final val lasher_projectile_ap = Value(431)
final val liberator_bomb_cluster_bomblet_projectile = Value(436)
final val liberator_bomb_cluster_projectile = Value(437)
final val liberator_bomb_projectile = Value(438)
final val maelstrom_grenade_damager = Value(464)
final val maelstrom_grenade_projectile = Value(465)
final val maelstrom_grenade_projectile_contact = Value(466)
final val maelstrom_stream_projectile = Value(467)
final val magcutter_projectile = Value(469)
final val melee_ammo_projectile = Value(541)
final val meteor_common = Value(543)
final val meteor_projectile_b_large = Value(544)
final val meteor_projectile_b_medium = Value(545)
final val meteor_projectile_b_small = Value(546)
final val meteor_projectile_large = Value(547)
final val meteor_projectile_medium = Value(548)
final val meteor_projectile_small = Value(549)
final val mine_projectile = Value(551)
final val mine_sweeper_projectile = Value(554)
final val mine_sweeper_projectile_enh = Value(555)
final val ntu_siphon_emp = Value(596)
final val oicw_little_buddy = Value(601)
final val oicw_projectile = Value(602)
final val pellet_gun_projectile = Value(631)
final val peregrine_dual_machine_gun_projectile = Value(639)
final val peregrine_mechhammer_projectile = Value(647)
final val peregrine_particle_cannon_projectile = Value(654)
final val peregrine_particle_cannon_radiation_cloud = Value(655) //radiation cloud
final val peregrine_rocket_pod_projectile = Value(657)
final val peregrine_sparrow_projectile = Value(661)
final val phalanx_av_projectile = Value(665)
final val phalanx_flak_projectile = Value(667)
final val phalanx_projectile = Value(669)
final val phoenix_missile_guided_projectile = Value(675)
final val phoenix_missile_projectile = Value(676)
final val plasma_cartridge_projectile = Value(678)
final val plasma_cartridge_projectile_b = Value(679)
final val plasma_grenade_projectile = Value(682)
final val plasma_grenade_projectile_B = Value(683)
final val pounder_projectile = Value(694)
final val pounder_projectile_enh = Value(695)
final val ppa_projectile = Value(696)
final val pulsar_ap_projectile = Value(702)
final val pulsar_projectile = Value(703)
final val quasar_projectile = Value(713)
final val radiator_cloud = Value(717) //radiation cloud
final val radiator_grenade_projectile = Value(718)
final val radiator_sticky_projectile = Value(719)
final val reaver_rocket_projectile = Value(723)
final val rocket_projectile = Value(735)
final val rocklet_flak_projectile = Value(738)
final val rocklet_jammer_projectile = Value(739)
final val scattercannon_projectile = Value(746)
final val scythe_projectile = Value(748)
final val scythe_projectile_slave = Value(749)
final val shotgun_shell_AP_projectile = Value(757)
final val shotgun_shell_projectile = Value(758)
final val six_shooter_projectile = Value(763)
final val skyguard_flak_cannon_projectile = Value(787)
final val sparrow_projectile = Value(792)
final val sparrow_secondary_projectile = Value(793)
final val spiker_projectile = Value(818)
final val spitfire_aa_ammo_projectile = Value(821)
final val spitfire_ammo_projectile = Value(824)
final val starfire_projectile = Value(831)
final val striker_missile_projectile = Value(840)
final val striker_missile_targeting_projectile = Value(841)
final val trek_projectile = Value(878)
final val vanu_sentry_turret_projectile = Value(944)
final val vulture_bomb_projectile = Value(988)
final val vulture_nose_bullet_projectile = Value(989)
final val vulture_tail_bullet_projectile = Value(991)
final val wasp_gun_projectile = Value(999)
final val wasp_rocket_projectile = Value(1001)
final val winchester_projectile = Value(1005)
}
}

View file

@ -15,7 +15,7 @@ class ProjectileDefinition(objectId: Int)
extends ObjectDefinition(objectId)
with DamageWithPosition {
/** ascertain that this object is a valid projectile type */
private val projectileType: Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
private val projectileType: Projectiles.Types.Value = Projectiles.Types(objectId) //let throw NoSuchElementException
/** how much faster (or slower) the projectile moves (m/s^2^) */
private var acceleration: Int = 0
/** when the acceleration stops being applied (s) */
@ -64,7 +64,7 @@ class ProjectileDefinition(objectId: Int)
Modifiers = DistanceDegrade
registerAs = "projectiles"
def ProjectileType: Projectiles.Value = projectileType
def ProjectileType: Projectiles.Types.Value = projectileType
def Acceleration: Int = acceleration
@ -176,7 +176,7 @@ class ProjectileDefinition(objectId: Int)
}
object ProjectileDefinition {
def apply(projectileType: Projectiles.Value): ProjectileDefinition = {
def apply(projectileType: Projectiles.Types.Value): ProjectileDefinition = {
new ProjectileDefinition(projectileType.id)
}

View file

@ -0,0 +1,41 @@
// Copyright (c) 2022 PSForever
package net.psforever.objects.definition.converter
import net.psforever.objects.ballistics.Projectile
import net.psforever.packet.game.objectcreate._
import net.psforever.types.PlanetSideGUID
import scala.util.{Success, Try}
class LittleBuddyProjectileConverter extends ObjectCreateConverter[Projectile]() {
override def ConstructorData(obj: Projectile): Try[LittleBuddyProjectileData] = lilBudData(obj)
override def DetailedConstructorData(obj: Projectile): Try[LittleBuddyProjectileData] = lilBudData(obj)
private def lilBudData(obj: Projectile): Try[LittleBuddyProjectileData] = {
Success(
LittleBuddyProjectileData(
CommonFieldDataWithPlacement(
PlacementData(
obj.Position,
obj.Orientation,
obj.Velocity
),
CommonFieldData(
obj.owner.Faction,
bops = false,
alternate = false,
v1 = true,
v2 = None,
jammered = false,
v4 = None,
v5 = None,
guid = PlanetSideGUID(0)
)
),
u2 = 0,
u4 = true
)
)
}
}

View file

@ -42,7 +42,7 @@ object EffectTarget {
def RepairSilo(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle =>
!GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth && v.History.exists(x => x.isInstanceOf[DamagingActivity] && x.time >= (System.currentTimeMillis() - 5000L))
!GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth && !v.History.takeWhile(System.currentTimeMillis() - _.time <= 5000L).exists(_.isInstanceOf[DamagingActivity])
case _ =>
false
}
@ -50,22 +50,7 @@ object EffectTarget {
def PadLanding(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle =>
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth && v.History.exists(x => x.isInstanceOf[DamagingActivity] && x.time >= (System.currentTimeMillis() - 5000000000L))
case _ =>
false
}
def AncientVehicleWeaponRecharge(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle =>
GlobalDefinitions.isCavernVehicle(v.Definition) && v.Health > 0 &&
v.Weapons.values
.map { _.Equipment }
.flatMap {
case Some(weapon: Tool) => weapon.AmmoSlots
case _ => Nil
}
.exists { slot => slot.Box.Capacity < slot.Definition.Magazine }
GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0 && v.Health < v.MaxHealth && !v.History.takeWhile(System.currentTimeMillis() - _.time <= 5000L).exists(_.isInstanceOf[DamagingActivity])
case _ =>
false
}
@ -135,5 +120,34 @@ object EffectTarget {
case _ =>
false
}
def AncientWeaponRecharge(target: PlanetSideGameObject): Boolean = {
target match {
case p: Player =>
(p.Holsters().map { _.Equipment }.flatten.toIterable ++ p.Inventory.Items.map { _.obj })
.flatMap {
case weapon: Tool => weapon.AmmoSlots
case _ => Nil
}
.exists { slot => slot.Box.Capacity < slot.Definition.Magazine }
case _ =>
false
}
}
def AncientVehicleWeaponRecharge(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle =>
GlobalDefinitions.isCavernVehicle(v.Definition) && v.Health > 0 &&
v.Weapons.values
.map { _.Equipment }
.flatMap {
case Some(weapon: Tool) => weapon.AmmoSlots
case _ => Nil
}
.exists { slot => slot.Box.Capacity < slot.Definition.Magazine }
case _ =>
false
}
}
}

View file

@ -3,28 +3,15 @@ package net.psforever.objects.serverobject.terminals
import net.psforever.objects.Player
import scala.concurrent.duration.{Duration, FiniteDuration}
/**
* The definition for any `Terminal` that is of a type "medical_terminal".
* This includes the functionality of the formal medical terminals and some of the cavern crystals.
* Do not confuse the game's internal "medical_terminal" object category and the actual `medical_terminal` object (529).
*/
class MedicalTerminalDefinition(objectId: Int) extends ProximityTerminalDefinition(objectId) {
private var interval: FiniteDuration = Duration(0, "seconds")
private var healAmount: Int = 0
private var armorAmount: Int = 0
def Interval: FiniteDuration = interval
def Interval_=(amount: Int): FiniteDuration = {
Interval_=(Duration(amount, "milliseconds"))
}
def Interval_=(amount: FiniteDuration): FiniteDuration = {
interval = amount
Interval
}
class MedicalTerminalDefinition(objectId: Int)
extends ProximityTerminalDefinition(objectId) {
private var healAmount: Int = 0
private var armorAmount: Int = 0
def HealAmount: Int = healAmount

View file

@ -6,6 +6,7 @@ import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.equipment.EffectTarget
import scala.collection.mutable
import scala.concurrent.duration.{Duration, FiniteDuration}
/**
* The definition mix-in for any game object that possesses a proximity-based effect.
@ -17,10 +18,22 @@ import scala.collection.mutable
trait ProximityDefinition {
this: ObjectDefinition =>
private var interval: FiniteDuration = Duration(0, "seconds")
private var useRadius: Float = 0f //TODO belongs on a wider range of object definitions
private val targetValidation: mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject => Boolean] =
new mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject => Boolean]()
def Interval: FiniteDuration = interval
def Interval_=(amount: Int): FiniteDuration = {
Interval_=(Duration(amount, "milliseconds"))
}
def Interval_=(amount: FiniteDuration): FiniteDuration = {
interval = amount
Interval
}
def UseRadius: Float = useRadius
def UseRadius_=(radius: Float): Float = {

View file

@ -3,6 +3,8 @@ package net.psforever.objects.serverobject.terminals
import akka.actor.{ActorRef, Cancellable}
import net.psforever.objects._
import net.psforever.objects.ballistics.{PlayerSource, VehicleSource}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target
@ -10,8 +12,12 @@ import net.psforever.objects.serverobject.damage.DamageableAmenity
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.objects.vital.{HealFromTerm, RepairFromTerm}
import net.psforever.packet.game.InventoryStateMessage
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.collection.mutable
import scala.concurrent.duration._
@ -92,6 +98,9 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
callbackList.lift(index) match {
case Some(cback) =>
cback ! ProximityUnit.Action(term, target)
if (ProximityTerminalControl.selectAndTryProximityUnitBehavior(cback, term, target)) {
Unuse(target, term.Zone.id)
}
case None =>
log.error(
s"improper callback registered for $target on $term in zone ${term.Owner.Continent}; this may be recoverable"
@ -141,12 +150,12 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
callbacks += callback
//activation
if (term.NumberUsers == 1 && hadNoUsers) {
val medDef = term.Definition.asInstanceOf[MedicalTerminalDefinition]
val tdef = term.Definition.asInstanceOf[ProximityDefinition]
import scala.concurrent.ExecutionContext.Implicits.global
terminalAction.cancel()
terminalAction = context.system.scheduler.scheduleWithFixedDelay(
500 milliseconds,
medDef.Interval,
tdef.Interval,
self,
ProximityTerminalControl.TerminalAction()
)
@ -166,7 +175,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
s"ProximityTerminal.Unuse: unit ${term.Definition.Name}@${term.GUID.guid} will cease operation on $target"
)
//remove callback
callbacks.remove(whereTarget)
callbacks.remove(whereTarget) ! ProximityUnit.StopAction(term, target)
//de-activation (global / local)
if (term.NumberUsers == 0 && hadUsers) {
terminalAction.cancel()
@ -203,4 +212,209 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
object ProximityTerminalControl {
private case class TerminalAction()
/**
* Determine which functionality to pursue by a generic proximity-functional unit given the target for its activity.
* @see `VehicleService:receive, ProximityUnit.Action`
* @param terminal the proximity-based unit
* @param target the object being affected by the unit
*/
def selectAndTryProximityUnitBehavior(
callback: ActorRef,
terminal: Terminal with ProximityUnit,
target: PlanetSideGameObject
): Boolean = {
(terminal.Definition, target) match {
case (_: MedicalTerminalDefinition, p: Player) => HealthAndArmorTerminal(terminal, p)
case (_: WeaponRechargeTerminalDefinition, p: Player) => WeaponRechargeTerminal(terminal, p)
case (_: MedicalTerminalDefinition, v: Vehicle) => VehicleRepairTerminal(terminal, v)
case (_: WeaponRechargeTerminalDefinition, v: Vehicle) => WeaponRechargeTerminal(terminal, v)
case _ => false
}
}
/**
* When standing on the platform of a(n advanced) medical terminal,
* restore the player's health and armor points (when they need their health and armor points restored).
* If the player is both fully healed and fully repaired, stop using the terminal.
* @param unit the medical terminal
* @param target the player being healed
*/
def HealthAndArmorTerminal(unit: Terminal with ProximityUnit, target: Player): Boolean = {
val medDef = unit.Definition.asInstanceOf[MedicalTerminalDefinition]
val healAmount = medDef.HealAmount
val healthFull: Boolean = if (healAmount != 0 && target.Health < target.MaxHealth) {
target.History(HealFromTerm(PlayerSource(target), healAmount, 0, medDef))
HealAction(target, healAmount)
} else {
true
}
val repairAmount = medDef.ArmorAmount
val armorFull: Boolean = if (repairAmount != 0 && target.Armor < target.MaxArmor) {
target.History(HealFromTerm(PlayerSource(target), 0, repairAmount, medDef))
ArmorRepairAction(target, repairAmount)
} else {
true
}
healthFull && armorFull
}
/**
* Restore, at most, a specific amount of health points on a player.
* Send messages to connected client and to events system.
* @param tplayer the player
* @param healValue the amount to heal;
* 10 by default
* @return whether the player can be repaired for any more health points
*/
def HealAction(tplayer: Player, healValue: Int = 10): Boolean = {
tplayer.Health = tplayer.Health + healValue
val zone = tplayer.Zone
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 0, tplayer.Health)
)
tplayer.Health == tplayer.MaxHealth
}
/**
* Restore, at most, a specific amount of personal armor points on a player.
* Send messages to connected client and to events system.
* @param tplayer the player
* @param repairValue the amount to repair;
* 10 by default
* @return whether the player can be repaired for any more armor points
*/
def ArmorRepairAction(tplayer: Player, repairValue: Int = 10): Boolean = {
tplayer.Armor = tplayer.Armor + repairValue
val zone = tplayer.Zone
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 4, tplayer.Armor)
)
tplayer.Armor == tplayer.MaxArmor
}
/**
* When driving a vehicle close to a rearm/repair silo,
* restore the vehicle's health points.
* If the vehicle is fully repaired, stop using the terminal.
* @param unit the terminal
* @param target the vehicle being repaired
*/
def VehicleRepairTerminal(unit: Terminal with ProximityUnit, target: Vehicle): Boolean = {
val medDef = unit.Definition.asInstanceOf[MedicalTerminalDefinition]
val healAmount = medDef.HealAmount
val maxHealth = target.MaxHealth
val noMoreHeal = if (!target.Destroyed && unit.Validate(target)) {
//repair vehicle
if (healAmount > 0 && target.Health < maxHealth) {
target.Health = target.Health + healAmount
target.History(RepairFromTerm(VehicleSource(target), healAmount, medDef))
val zone = target.Zone
zone.VehicleEvents ! VehicleServiceMessage(
zone.id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 0, target.Health)
)
target.Health == maxHealth
} else {
true
}
} else {
true
}
noMoreHeal
}
/**
* When standing in a friendly SOI whose facility is under the influence of an Ancient Weapon Module benefit,
* and the player is in possession of Ancient weaponnry whose magazine is not full,
* restore some ammunition to its magazine.
* If no valid weapons are discovered or the discovered valid weapons have full magazines, stop using the terminal.
* @param unit the terminal
* @param target the player with weapons being recharged
*/
def WeaponRechargeTerminal(unit: Terminal with ProximityUnit, target: Player): Boolean = {
val result = WeaponsBeingRechargedWithSomeAmmunition(
unit.Definition.asInstanceOf[WeaponRechargeTerminalDefinition].AmmoAmount,
target.Holsters().map { _.Equipment }.flatten.toIterable ++ target.Inventory.Items.map { _.obj }
)
val events = unit.Zone.AvatarEvents
val channel = target.Name
result.foreach { case (weapon, slots) =>
slots.foreach { slot =>
events ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(slot.Box.GUID, weapon.GUID, slot.Box.Capacity))
)
}
}
!result.unzip._2.flatten.exists { slot => slot.Magazine < slot.MaxMagazine() }
}
/**
* When driving close to a rearm/repair silo whose facility is under the influence of an Ancient Weapon Module benefit,
* and the vehicle is an Ancient vehicle with mounted weaponry whose magazine(s) is not full,
* restore some ammunition to the magazine(s).
* If no valid weapons are discovered or the discovered valid weapons have full magazines, stop using the terminal.
* @param unit the terminal
* @param target the vehicle with weapons being recharged
*/
def WeaponRechargeTerminal(unit: Terminal with ProximityUnit, target: Vehicle): Boolean = {
val result = WeaponsBeingRechargedWithSomeAmmunition(
unit.Definition.asInstanceOf[WeaponRechargeTerminalDefinition].AmmoAmount,
target.Weapons.values.collect { case e if e.Equipment.nonEmpty => e.Equipment.get }
)
val events = unit.Zone.VehicleEvents
val channel = target.Actor.toString
result.foreach { case (weapon, slots) =>
slots.foreach { slot =>
events ! VehicleServiceMessage(
channel,
VehicleAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(slot.Box.GUID, weapon.GUID, slot.Box.Capacity))
)
}
}
!result.unzip._2.flatten.exists { slot => slot.Magazine < slot.MaxMagazine() }
}
/**
* Collect all weapons with magazines that need to have ammunition reloaded,
* and reload some ammunition into them.
* @param ammoAdded the amount of ammo to be added to a weapon
* @param equipment the equipment being considered;
* weapons whose ammo will be increased will be isolated
* @return na
*/
def WeaponsBeingRechargedWithSomeAmmunition(
ammoAdded: Int,
equipment: Iterable[Equipment]
): Iterable[(Tool, Iterable[Tool.FireModeSlot])] = {
equipment
.collect {
case weapon: Tool
if weapon.AmmoSlots.exists(slot => slot.Box.Capacity < slot.Definition.Magazine) =>
(weapon, WeaponAmmoRecharge(ammoAdded, weapon.AmmoSlots))
}
}
/**
* Collect all magazines from this weapon that need to have ammunition reloaded,
* and reload some ammunition into them.
* @param ammoAdded the amount of ammo to be added to a weapon
* @param slots the vehicle with weapons being recharged
* @return ammunition slots that were affected
*/
def WeaponAmmoRecharge(
ammoAdded: Int,
slots: List[Tool.FireModeSlot]
): List[Tool.FireModeSlot] = {
val unfilledSlots = slots.filter { slot => slot.Magazine < slot.MaxMagazine() }
if (unfilledSlots.nonEmpty) {
unfilledSlots.foreach { slot => slot.Box.Capacity = slot.Box.Capacity + ammoAdded }
unfilledSlots
} else {
Nil
}
}
}

View file

@ -9,6 +9,8 @@ import net.psforever.objects.Player
*
* @param objectId the object's identifier number
*/
class ProximityTerminalDefinition(objectId: Int) extends TerminalDefinition(objectId) with ProximityDefinition {
class ProximityTerminalDefinition(objectId: Int)
extends TerminalDefinition(objectId)
with ProximityDefinition {
def Request(player: Player, msg: Any): Terminal.Exchange = Terminal.NoDeal()
}

View file

@ -81,7 +81,7 @@ trait ProximityUnit {
* @return `true`, if the entity passes the validation tests;
* `false`, otherwise
*/
def Validate(radius: Float, validations: Seq[(PlanetSideGameObject) => Boolean])(
def Validate(radius: Float, validations: Seq[PlanetSideGameObject => Boolean])(
target: PlanetSideGameObject
): Boolean = {
//org.log4s.getLogger("ProximityUnit").info(s"vehicle: ${Owner.Position}, terminal: $Position, target: ${target.Position}, toOwner: ${Vector3.Distance(Position, Owner.Position)}, toTarget: ${Vector3.Distance(Position, target.Position)}")
@ -93,4 +93,6 @@ trait ProximityUnit {
object ProximityUnit {
final case class Action(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject)
final case class StopAction(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject)
}

View file

@ -0,0 +1,21 @@
// Copyright (c) 2022 PSForever
package net.psforever.objects.serverobject.terminals
import net.psforever.objects.Player
/**
* na
*/
class WeaponRechargeTerminalDefinition(objectId: Int)
extends ProximityTerminalDefinition(objectId) {
private var ammoAmount: Int = 1
def AmmoAmount: Int = ammoAmount
def AmmoAmount_=(amount: Int): Int = {
ammoAmount = amount
AmmoAmount
}
override def Request(player: Player, msg: Any): Terminal.Exchange = Terminal.NoDeal()
}

View file

@ -162,11 +162,13 @@ trait VitalityDefinition extends DamageModifiers {
/**
* damage that is inherent to the object, used for explosions, mainly
*/
var innateDamage: Option[DamageWithPosition] = None
private var _innateDamage: Option[DamageWithPosition] = None
def innateDamage: Option[DamageWithPosition] = _innateDamage
def innateDamage_=(combustion: DamageWithPosition): Option[DamageWithPosition] = {
innateDamage = Some(combustion)
innateDamage
_innateDamage = Some(combustion)
_innateDamage
}
val collision: CollisionData = new CollisionData()

View file

@ -9,6 +9,6 @@ package net.psforever.objects.vital.base
object DamageType extends Enumeration(1) {
type Type = Value
//"one" (numerical 1 in the ADB) corresponds to objects that explode
//"one" (numerical 1 in the ADB) corresponds to objects that explode and kill fields
final val Direct, Splash, Lash, Radiation, Aggravated, One, Siphon, None = Value
}

View file

@ -109,3 +109,13 @@ case object ExplodingRadialDegrade extends ExplodingDamageModifiers.Mod {
}
}
}
case object ExplosionDamagesOnlyAbove extends ExplodingDamageModifiers.Mod {
def calculate(damage: Int, data: DamageInteraction, cause: ExplodingEntityReason): Int = {
if (data.target.Position.z <= data.hitPos.z) {
damage
} else {
0
}
}
}

View file

@ -11,7 +11,7 @@ import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResist
final case class PainboxReason(entity: Painbox) extends DamageReason {
private val definition = entity.Definition
assert(definition.innateDamage.nonEmpty, "causal entity does not emit pain field")
assert(definition.innateDamage.nonEmpty, s"causal entity '${definition.Name}' does not emit pain field")
def source: DamageWithPosition = definition.innateDamage.get

View file

@ -11,7 +11,6 @@ import net.psforever.objects.vital.resolution.DamageAndResistance
/**
* A wrapper for a "damage source" in damage calculations
* that parameterizes information necessary to explain a radiation cloud.
* @param resolution how the damage is processed
* @param projectile the projectile that caused the damage
* @param damageModel the model to be utilized in these calculations;
* typically, but not always, defined by the target
@ -55,9 +54,8 @@ object RadiationDamageModifiers {
}
/**
* If the damge is caused by a projectile that emits a field that permeates vehicle armor,
* If the damage is caused by a projectile that emits a field that permeates vehicle armor,
* determine by how much the traversed armor's shielding reduces the damage.
* Infantry take damage, reduced only if one is equipped with a mechanized assault exo-suit.
*/
case object ShieldAgainstRadiation extends RadiationDamageModifiers.Mod {
def calculate(damage: Int, data: DamageInteraction, cause: RadiationReason): Int = {

View file

@ -330,7 +330,7 @@ case object FlailDistanceDamageBoost extends ProjectileDamageModifiers.Mod {
}
/**
* If the damge is caused by a projectile that emits a field that permeates vehicle armor,
* If the damge is caused by a projectile that emits a field that permeates armor,
* determine by how much the traversed armor's shielding reduces the damage.
* Infantry take damage, reduced only if one is equipped with a mechanized assault exo-suit.
*/

View file

@ -30,8 +30,9 @@ trait DamageProperties
private var useDamage1Subtract: Boolean = false
/** some other entity confers damage;
* a set value should be the damager's object uid
* usually corresponding to a projectile */
private var damageProxy: Option[Int] = None
* usually corresponding to a projectile;
* also used to produce staged projectiles */
private var damageProxy: List[Int] = Nil
/** na;
* currently used with jammer properties only */
private var additionalEffect: Boolean = false
@ -88,11 +89,14 @@ trait DamageProperties
DamageToBattleframeOnly
}
def DamageProxy : Option[Int] = damageProxy
def DamageProxy : List[Int] = damageProxy
def DamageProxy_=(proxyObjectId : Int) : Option[Int] = DamageProxy_=(Some(proxyObjectId))
def DamageProxy_=(proxyObjectId: Int): List[Int] = {
damageProxy = damageProxy :+ proxyObjectId
DamageProxy
}
def DamageProxy_=(proxyObjectId : Option[Int]) : Option[Int] = {
def DamageProxy_=(proxyObjectId: List[Int]): List[Int] = {
damageProxy = proxyObjectId
DamageProxy
}

View file

@ -1257,10 +1257,27 @@ object Zone {
source: PlanetSideGameObject with FactionAffinity with Vitality,
target: PlanetSideGameObject with FactionAffinity with Vitality
): DamageInteraction = {
explosionDamage(instigation, target.Position)(source, target)
}/**
* na
* @param instigation what previous event happened, if any, that caused this explosion
* @param explosionPosition the coordinates of the detected explosion
* @param source a game object that represents the source of the explosion
* @param target a game object that is affected by the explosion
* @return a `DamageInteraction` object
*/
def explosionDamage(
instigation: Option[DamageResult],
explosionPosition: Vector3
)
(
source: PlanetSideGameObject with FactionAffinity with Vitality,
target: PlanetSideGameObject with FactionAffinity with Vitality
): DamageInteraction = {
DamageInteraction(
SourceEntry(target),
ExplodingEntityReason(source, target.DamageModel, instigation),
target.Position
explosionPosition
)
}

View file

@ -143,6 +143,10 @@ class ZoneProjectileActor(
val (clarifiedFilterGuid, duration) = if (definition.radiation_cloud) {
zone.blockMap.addTo(projectile)
(Service.defaultPlayerGUID, projectile.profile.Lifespan seconds)
} else if (definition.RemoteClientData == (0,0)) {
//remote projectiles that are not radiation clouds have lifespans controlled by the controller (user)
//this projectile has defaulted remote client data
(Service.defaultPlayerGUID, projectile.profile.Lifespan * 1.5f seconds)
} else {
//remote projectiles that are not radiation clouds have lifespans controlled by the controller (user)
//if the controller fails, the projectile has a bit more than its normal lifespan before automatic clean up
@ -188,6 +192,11 @@ class ZoneProjectileActor(
zone.id,
AvatarAction.ObjectDelete(PlanetSideGUID(0), projectile_guid, 2)
)
} else if (projectile.Definition.RemoteClientData == (0,0)) {
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.ObjectDelete(PlanetSideGUID(0), projectile_guid, 2)
)
} else {
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,

View file

@ -0,0 +1,40 @@
// Copyright (c) 2022 PSForever
package net.psforever.packet.game.objectcreate
import net.psforever.packet.Marshallable
import scodec.{Attempt, Codec}
import scodec.codecs._
import shapeless.{::, HNil}
final case class LittleBuddyProjectileData(
data: CommonFieldDataWithPlacement,
u2: Int,
u4: Boolean
) extends ConstructorData {
assert(data.pos.vel.nonEmpty, "oicw little buddy object always requires velocity to be defined")
/**
* The length of the little buddy data is functionally `32u`
* after all other fields are accounted for
* but the packet decode demands an additional bit be accounted for.
* @return the number of bits necessary to measure an object of this class
*/
override def bitsize: Long = 33L + data.bitsize
}
object LittleBuddyProjectileData extends Marshallable[LittleBuddyProjectileData] {
implicit val codec: Codec[LittleBuddyProjectileData] = (
("data" | CommonFieldDataWithPlacement.codec) ::
("unk2" | uint24L) ::
uint(bits = 7) ::
("unk4" | bool)
).exmap[LittleBuddyProjectileData](
{
case data :: u2 :: 31 :: u4 :: HNil =>
Attempt.successful(LittleBuddyProjectileData(data, u2, u4))
},
{
case LittleBuddyProjectileData(data, u2, u4) =>
Attempt.successful(data :: u2 :: 31 :: u4 :: HNil)
}
)
}

View file

@ -669,7 +669,8 @@ object ObjectClass {
def selectDataDroppedDetailedCodec(objClass: Int): Codec[ConstructorData] =
(objClass: @switch) match {
//special cases
case ObjectClass.avatar => ConstructorData(DetailedPlayerData.codec(true), "avatar")
case ObjectClass.avatar => ConstructorData(DetailedPlayerData.codec(position_defined = true), "avatar")
case ObjectClass.oicw_little_buddy => ConstructorData(LittleBuddyProjectileData.codec, "projectile")
//defer to other codec selection
case _ => selectDataDetailedCodec(objClass)
}
@ -1240,7 +1241,7 @@ object ObjectClass {
case ObjectClass.meteor_projectile_small => ConstructorData(RemoteProjectileData.codec, "meteor")
case ObjectClass.peregrine_particle_cannon_radiation_cloud => ConstructorData(RadiationCloudData.codec, "radiation cloud")
case ObjectClass.phoenix_missile_guided_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
case ObjectClass.oicw_little_buddy => ConstructorData(RemoteProjectileData.codec, "projectile")
case ObjectClass.oicw_little_buddy => ConstructorData(LittleBuddyProjectileData.codec, "projectile")
case ObjectClass.oicw_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")
case ObjectClass.radiator_cloud => ConstructorData(RadiationCloudData.codec, "radiation cloud")
case ObjectClass.sparrow_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")

View file

@ -9,14 +9,15 @@ 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)
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)
//the oicw_little_buddy is handled by its own transcoder
}
object FlightPhysics extends Enumeration {
@ -67,7 +68,7 @@ object RemoteProjectileData extends Marshallable[RemoteProjectileData] {
("u1" | uint16) ::
("u2" | uint8) ::
("unk3" | FlightPhysics.codec) ::
("unk4" | uint(3)) ::
("unk4" | uint(bits = 3)) ::
("unk5" | uint2)
).exmap[RemoteProjectileData](
{

View file

@ -2,16 +2,12 @@
package net.psforever.services.vehicle
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.{GlobalDefinitions, Tool, Vehicle}
import net.psforever.objects.ballistics.VehicleSource
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.{MedicalTerminalDefinition, ProximityUnit}
import net.psforever.objects.vital.RepairFromTerm
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.services.vehicle.support.TurretUpgrader
import net.psforever.types.{DriveState, PlanetSideGUID}
import net.psforever.types.DriveState
import net.psforever.services.{GenericEventBus, Service}
class VehicleService(zone: Zone) extends Actor {
@ -395,51 +391,6 @@ class VehicleService(zone: Zone) extends Actor {
)
)
//from ProximityTerminalControl (?)
case ProximityUnit.Action(term, target: Vehicle) =>
val medDef = term.Definition.asInstanceOf[MedicalTerminalDefinition]
val healAmount = medDef.HealAmount
if (!target.Destroyed && term.Validate(target)) {
//repair vehicle
if (healAmount > 0 && target.Health < target.MaxHealth) {
val healAmount = medDef.HealAmount
target.Health = target.Health + healAmount
target.History(RepairFromTerm(VehicleSource(target), healAmount, medDef))
VehicleEvents.publish(
VehicleServiceResponse(
s"/${term.Continent}/Vehicle",
PlanetSideGUID(0),
VehicleResponse.PlanetsideAttribute(target.GUID, 0, target.Health)
)
)
}
//recharge ammunition of cavern vehicles
if (GlobalDefinitions.isCavernVehicle(target.Definition) && term.Definition == GlobalDefinitions.recharge_terminal) {
//TODO check cavern module benefits on facility; unlike facility benefits, it's faked for now
val channel = s"/${target.Actor.toString}/Vehicle"
val parent = target.GUID
val excludeNone = Service.defaultPlayerGUID
target.Weapons.values
.map { _.Equipment }
.collect { case Some(weapon: Tool) =>
weapon.AmmoSlots
.foreach { slot =>
val box = slot.Box
if (box.Capacity < slot.Definition.Magazine) {
val capacity = box.Capacity += 1
VehicleEvents.publish(
VehicleServiceResponse(
channel,
excludeNone,
VehicleResponse.InventoryState2(box.GUID, parent, capacity)
)
)
}
}
}
}
}
case msg =>
log.warn(s"Unhandled message $msg from ${sender()}")
}

View file

@ -295,6 +295,19 @@ object Zones {
)
)
)
if (facilityTypes.contains(structure.objectType)) {
//major overworld facilities have an intrinsic terminal that occasionally recharges ancient weapons
val buildingGuid = structure.guid
val terminalGuid = buildingGuid + 1
zoneMap.addLocalObject(
buildingGuid + 1,
ProximityTerminal.Constructor(
structure.position,
GlobalDefinitions.recharge_terminal_weapon_module
),
owningBuildingGuid = buildingGuid
)
}
}
val filteredZoneEntities =
data.filter { _.owner.contains(structure.id) } ++

View file

@ -2,7 +2,7 @@
package game.objectcreate
import net.psforever.packet.PacketCoding
import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.{ObjectCreateDetailedMessage, ObjectCreateMessage}
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
import org.specs2.mutable._
@ -11,6 +11,7 @@ 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"
val string_oicw_little_buddy = hex"18 ef000000 aca 3d0e 1ef35 d9417 2511 00 0f 21 d3bf0d1bc38900000000000fc"
"RemoteProjectileData" should {
"decode (striker_missile_targeting_projectile)" in {
@ -33,7 +34,6 @@ class RemoteProjectileDataTest extends Specification {
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
@ -67,7 +67,6 @@ class RemoteProjectileDataTest extends Specification {
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
@ -81,6 +80,37 @@ class RemoteProjectileDataTest extends Specification {
}
}
"decode (oicw_little_buddy)" in {
PacketCoding.decodePacket(string_oicw_little_buddy).require match {
case ObjectCreateDetailedMessage(len, cls, guid, parent, data) =>
len mustEqual 239
cls mustEqual ObjectClass.oicw_little_buddy
guid mustEqual PlanetSideGUID(3645)
parent.isDefined mustEqual false
data match {
case LittleBuddyProjectileData(dat, u2, u4) =>
dat.pos.coord mustEqual Vector3(3046.2344f, 3715.6953f, 68.578125f)
dat.pos.orient mustEqual Vector3(0, 317.8125f, 357.1875f)
dat.pos.vel.contains(Vector3(-10.0125f, 101.475f, -101.7f)) mustEqual true
dat.data.faction mustEqual PlanetSideEmpire.NC
dat.data.bops mustEqual false
dat.data.alternate mustEqual false
dat.data.v1 mustEqual true
dat.data.v2.isEmpty mustEqual true
dat.data.jammered mustEqual false
dat.data.v4.isEmpty mustEqual true
dat.data.v5.isEmpty mustEqual true
dat.data.guid mustEqual PlanetSideGUID(0)
u2 mustEqual 0
u4 mustEqual true
case _ =>
ko
}
case _ =>
ko
}
}
"encode (striker_missile_targeting_projectile)" in {
val obj = RemoteProjectileData(
CommonFieldDataWithPlacement(
@ -101,26 +131,53 @@ class RemoteProjectileDataTest extends Specification {
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
"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)
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)
}
"encode (oicw_little_buddy)" in {
val obj = LittleBuddyProjectileData(
CommonFieldDataWithPlacement(
PlacementData(Vector3(3046.2344f, 3715.6953f, 68.578125f),
Vector3(0, 317.8125f, 357.1875f),
Some(Vector3(-10.0125f, 101.475f, -101.7f))
),
CommonFieldData(
PlanetSideEmpire.NC,
bops = false,
alternate = false,
v1 = true,
v2 = None,
jammered = false,
v4 = None,
v5 = None,
guid = PlanetSideGUID(0)
)
),
u2 = 0,
u4 = true
)
val msg = ObjectCreateDetailedMessage(ObjectClass.oicw_little_buddy, PlanetSideGUID(3645), obj)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string_oicw_little_buddy
}
}
}

View file

@ -921,7 +921,7 @@ class DamageModelTests extends Specification {
}
object DamageModelTests {
final val projectile = new ProjectileDefinition(Projectiles.heavy_grenade_projectile.id) {
final val projectile = new ProjectileDefinition(Projectiles.Types.heavy_grenade_projectile.id) {
Damage0 = 50
Damage1 = 82
Damage2 = 82

View file

@ -26,7 +26,7 @@ class ProjectileTest extends Specification {
"define (default)" in {
val obj = new ProjectileDefinition(31) //9mmbullet_projectile
obj.ProjectileType mustEqual Projectiles.bullet_9mm_projectile
obj.ProjectileType mustEqual Projectiles.Types.bullet_9mm_projectile
obj.ObjectId mustEqual 31
obj.Damage0 mustEqual 0
obj.Damage1 mustEqual 0
@ -84,9 +84,9 @@ class ProjectileTest extends Specification {
}
"define (failure)" in {
Projectiles(31) mustEqual Projectiles.bullet_9mm_projectile
Projectiles.Types(31) mustEqual Projectiles.Types.bullet_9mm_projectile
try {
ProjectileDefinition(Projectiles.bullet_9mm_projectile) //passes
ProjectileDefinition(Projectiles.Types.bullet_9mm_projectile) //passes
} catch {
case _: NoSuchElementException =>
ko

View file

@ -14,7 +14,7 @@ import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.environment._
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.VehicleLockState
import net.psforever.objects.vehicles.control.{CargoCarrierControl, VehicleControl}
import net.psforever.objects.vehicles.control.{/*CargoCarrierControl, */VehicleControl}
import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game._

View file

@ -241,6 +241,7 @@ class ProximityTerminalControlTwoUsersTest extends ActorTest {
)
)
avatar.Continent = "test"
avatar.Zone = zone
avatar.Spawn()
avatar.Health = 50
val avatar2 =
@ -249,12 +250,13 @@ class ProximityTerminalControlTwoUsersTest extends ActorTest {
ProximityTest.avatarId.getAndIncrement(),
"TestCharacter2",
PlanetSideEmpire.VS,
CharacterSex.Female,
CharacterSex.Male,
1,
CharacterVoice.Voice1
)
)
avatar2.Continent = "test"
avatar2.Zone = zone
avatar2.Spawn()
avatar2.Health = 50
@ -266,6 +268,7 @@ class ProximityTerminalControlTwoUsersTest extends ActorTest {
val probe1 = new TestProbe(system, "local-events")
val probe2 = new TestProbe(system, "target-callback-1")
val probe3 = new TestProbe(system, "target-callback-2")
zone.AvatarEvents = new TestProbe(system, "avatar-events-throwaway").ref
zone.LocalEvents = probe1.ref
"not send out a start message if not the first user" in {
@ -274,7 +277,7 @@ class ProximityTerminalControlTwoUsersTest extends ActorTest {
terminal.Actor.tell(CommonMessages.Use(avatar, Some(avatar)), probe2.ref)
probe1.expectMsgClass(1 second, classOf[Terminal.StartProximityEffect])
probe2.expectMsgClass(1 second, classOf[ProximityUnit.Action])
probe2.expectMsgClass(5 second, classOf[ProximityUnit.Action])
terminal.Actor.tell(CommonMessages.Use(avatar2, Some(avatar2)), probe3.ref)
probe1.expectNoMessage(1 second)
@ -313,15 +316,17 @@ class ProximityTerminalControlStopTest extends ActorTest {
)
)
avatar.Continent = "test"
avatar.Zone = zone
avatar.Spawn()
avatar.Health = 50
avatar.Health = 1
avatar.GUID = PlanetSideGUID(1)
terminal.GUID = PlanetSideGUID(2)
terminal.Actor ! Service.Startup()
expectNoMessage(500 milliseconds) //spacer
val probe1 = new TestProbe(system, "local-events")
val probe2 = new TestProbe(system, "target-callback-1")
val probe2 = new TestProbe(system, "target-callback")
zone.AvatarEvents = new TestProbe(system, "avatar-events-throwaway").ref
zone.LocalEvents = probe1.ref
"send out a stop message" in {
@ -333,6 +338,14 @@ class ProximityTerminalControlStopTest extends ActorTest {
probe2.expectMsgClass(1 second, classOf[ProximityUnit.Action])
terminal.Actor ! CommonMessages.Unuse(avatar, Some(avatar))
probe2.fishForMessage(2.seconds, hint = "could not find StopAction") {
case _ : ProximityUnit.Action => false
case _ => true
} match {
case _ : ProximityUnit.StopAction => ;
case out => assert(false, s"last message $out is not StopAction")
}
//probe2.expectMsgClass(1 second, classOf[ProximityUnit.StopAction])
probe1.expectMsgClass(1 second, classOf[Terminal.StopProximityEffect])
assert(terminal.NumberUsers == 0)
}