diff --git a/.codecov.yml b/.codecov.yml index ca48a57c3..c12180862 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -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" diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index adb16b4dc..8616a6a48 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -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)) } } diff --git a/src/main/scala/net/psforever/objects/DummyExplodingEntity.scala b/src/main/scala/net/psforever/objects/DummyExplodingEntity.scala new file mode 100644 index 000000000..5f5336852 --- /dev/null +++ b/src/main/scala/net/psforever/objects/DummyExplodingEntity.scala @@ -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) +} diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index e967d87b7..38522f159 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -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 diff --git a/src/main/scala/net/psforever/objects/Tool.scala b/src/main/scala/net/psforever/objects/Tool.scala index 173513f12..312b781f6 100644 --- a/src/main/scala/net/psforever/objects/Tool.scala +++ b/src/main/scala/net/psforever/objects/Tool.scala @@ -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 diff --git a/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala b/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala index bc8debcfa..a3c5a10cd 100644 --- a/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala +++ b/src/main/scala/net/psforever/objects/ballistics/InteractWithRadiationClouds.scala @@ -62,7 +62,7 @@ class InteractWithRadiationClouds( RadiationReason( ProjectileQuality.modifiers(projectile, DamageResolution.Radiation, t, t.Position, user), t.DamageModel, - 1f + 0f ), position ).calculate() diff --git a/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala b/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala index 9dceecc20..6fe4f49c4 100644 --- a/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala +++ b/src/main/scala/net/psforever/objects/ballistics/Projectiles.scala @@ -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) + } } diff --git a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala index 28253156a..c60a9f182 100644 --- a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala +++ b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala @@ -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) } diff --git a/src/main/scala/net/psforever/objects/definition/converter/LittleBuddyProjectileConverter.scala b/src/main/scala/net/psforever/objects/definition/converter/LittleBuddyProjectileConverter.scala new file mode 100644 index 000000000..d7d061357 --- /dev/null +++ b/src/main/scala/net/psforever/objects/definition/converter/LittleBuddyProjectileConverter.scala @@ -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 + ) + ) + } +} diff --git a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala index 509134a9f..bb8a6d23f 100644 --- a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala +++ b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala @@ -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 + } } } diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala index e59752b82..fb6f14509 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/MedicalTerminalDefinition.scala @@ -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 diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala index 1ddcfacb3..adb0f99e1 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityDefinition.scala @@ -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 = { diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 04ded0e60..463ce0611 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -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 + } + } } diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalDefinition.scala index 27e3cf1fc..f45fe6f82 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalDefinition.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalDefinition.scala @@ -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() } diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala index 7892d3bb2..07d0bb6a8 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityUnit.scala @@ -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) } diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/WeaponRechargeTerminalDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/WeaponRechargeTerminalDefinition.scala new file mode 100644 index 000000000..0eabe7a69 --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/WeaponRechargeTerminalDefinition.scala @@ -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() +} diff --git a/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala b/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala index f3a41f11d..6d6db09c0 100644 --- a/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala +++ b/src/main/scala/net/psforever/objects/vital/VitalityDefinition.scala @@ -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() diff --git a/src/main/scala/net/psforever/objects/vital/base/DamageType.scala b/src/main/scala/net/psforever/objects/vital/base/DamageType.scala index a6aad0d7f..7372d62f0 100644 --- a/src/main/scala/net/psforever/objects/vital/base/DamageType.scala +++ b/src/main/scala/net/psforever/objects/vital/base/DamageType.scala @@ -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 } diff --git a/src/main/scala/net/psforever/objects/vital/etc/ExplodingEntityReason.scala b/src/main/scala/net/psforever/objects/vital/etc/ExplodingEntityReason.scala index 6e9984cd8..c86eac691 100644 --- a/src/main/scala/net/psforever/objects/vital/etc/ExplodingEntityReason.scala +++ b/src/main/scala/net/psforever/objects/vital/etc/ExplodingEntityReason.scala @@ -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 + } + } +} diff --git a/src/main/scala/net/psforever/objects/vital/etc/PainboxReason.scala b/src/main/scala/net/psforever/objects/vital/etc/PainboxReason.scala index 3397ce747..f02b2e38a 100644 --- a/src/main/scala/net/psforever/objects/vital/etc/PainboxReason.scala +++ b/src/main/scala/net/psforever/objects/vital/etc/PainboxReason.scala @@ -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 diff --git a/src/main/scala/net/psforever/objects/vital/etc/RadiationReason.scala b/src/main/scala/net/psforever/objects/vital/etc/RadiationReason.scala index a69be08e6..011cdb5a7 100644 --- a/src/main/scala/net/psforever/objects/vital/etc/RadiationReason.scala +++ b/src/main/scala/net/psforever/objects/vital/etc/RadiationReason.scala @@ -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 = { diff --git a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala index ab2a54902..5e6ba3a11 100644 --- a/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala +++ b/src/main/scala/net/psforever/objects/vital/projectile/ProjectileDamageModifierFunctions.scala @@ -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. */ diff --git a/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala index e3c85da3a..3eb0cc623 100644 --- a/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala +++ b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala @@ -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 } diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 032e02c07..a3ad9321f 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -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 ) } diff --git a/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala index f8c5b0186..1fc6deb58 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala @@ -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, diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/LittleBuddyProjectileData.scala b/src/main/scala/net/psforever/packet/game/objectcreate/LittleBuddyProjectileData.scala new file mode 100644 index 000000000..a47930061 --- /dev/null +++ b/src/main/scala/net/psforever/packet/game/objectcreate/LittleBuddyProjectileData.scala @@ -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) + } + ) +} diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala index c420e59b5..1479d4d13 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala @@ -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") diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/RemoteProjectileData.scala b/src/main/scala/net/psforever/packet/game/objectcreate/RemoteProjectileData.scala index 096f8efed..6f6570e23 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/RemoteProjectileData.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/RemoteProjectileData.scala @@ -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]( { diff --git a/src/main/scala/net/psforever/services/vehicle/VehicleService.scala b/src/main/scala/net/psforever/services/vehicle/VehicleService.scala index effcb1912..d4d18a491 100644 --- a/src/main/scala/net/psforever/services/vehicle/VehicleService.scala +++ b/src/main/scala/net/psforever/services/vehicle/VehicleService.scala @@ -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()}") } diff --git a/src/main/scala/net/psforever/zones/Zones.scala b/src/main/scala/net/psforever/zones/Zones.scala index 10b71bf1b..2b0a7738f 100644 --- a/src/main/scala/net/psforever/zones/Zones.scala +++ b/src/main/scala/net/psforever/zones/Zones.scala @@ -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) } ++ diff --git a/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala b/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala index dc3d062e0..f56585848 100644 --- a/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala +++ b/src/test/scala/game/objectcreate/RemoteProjectileDataTest.scala @@ -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 + } } } diff --git a/src/test/scala/objects/DamageModelTests.scala b/src/test/scala/objects/DamageModelTests.scala index 44ebb4021..be46e3ded 100644 --- a/src/test/scala/objects/DamageModelTests.scala +++ b/src/test/scala/objects/DamageModelTests.scala @@ -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 diff --git a/src/test/scala/objects/ProjectileTest.scala b/src/test/scala/objects/ProjectileTest.scala index cf57cbbea..62d1e9caa 100644 --- a/src/test/scala/objects/ProjectileTest.scala +++ b/src/test/scala/objects/ProjectileTest.scala @@ -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 diff --git a/src/test/scala/objects/VehicleControlTest.scala b/src/test/scala/objects/VehicleControlTest.scala index 377b0d2d3..6e5ba11ec 100644 --- a/src/test/scala/objects/VehicleControlTest.scala +++ b/src/test/scala/objects/VehicleControlTest.scala @@ -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._ diff --git a/src/test/scala/objects/terminal/ProximityTest.scala b/src/test/scala/objects/terminal/ProximityTest.scala index 8df74b1c6..f3c89992a 100644 --- a/src/test/scala/objects/terminal/ProximityTest.scala +++ b/src/test/scala/objects/terminal/ProximityTest.scala @@ -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) }