diff --git a/server/src/test/scala/actor/service/AvatarServiceTest.scala b/server/src/test/scala/actor/service/AvatarServiceTest.scala
index f912c0f0..4f9d2365 100644
--- a/server/src/test/scala/actor/service/AvatarServiceTest.scala
+++ b/server/src/test/scala/actor/service/AvatarServiceTest.scala
@@ -5,7 +5,7 @@ import akka.actor.Props
import akka.routing.RandomPool
import actor.base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData}
import net.psforever.packet.game.{ObjectCreateMessage, PlayerStateMessageUpstream}
@@ -17,6 +17,7 @@ import scala.concurrent.duration._
import akka.actor.typed.scaladsl.adapter._
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.Avatar
+import net.psforever.objects.guid.source.LimitedNumberSource
class AvatarService1Test extends ActorTest {
"AvatarService" should {
@@ -511,10 +512,13 @@ class AvatarReleaseTest extends ActorTest {
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) }
}
+ val guid1: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
+ zone.GUID(guid1)
val service = system.actorOf(Props(classOf[AvatarService], zone), "release-test-service")
- val taskResolver = system.actorOf(Props[TaskResolver](), "release-test-resolver")
zone.actor = system.spawn(ZoneActor(zone), "release-test-zone")
val obj = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
+ guid1.register(obj)
+ guid1.register(obj.Slot(5).Equipment.get)
obj.Continent = "test"
obj.Release
@@ -523,7 +527,6 @@ class AvatarReleaseTest extends ActorTest {
expectNoMessage(100 milliseconds) //spacer
service ! Service.Join("test")
- taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMessage(200 milliseconds) //spacer
@@ -561,10 +564,13 @@ class AvatarReleaseEarly1Test extends ActorTest {
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) }
}
+ val guid1: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
+ zone.GUID(guid1)
val service = system.actorOf(Props(classOf[AvatarService], zone), "release-test-service")
- val taskResolver = system.actorOf(Props[TaskResolver](), "release-test-resolver")
zone.actor = system.spawn(ZoneActor(zone), "release-test-zone")
val obj = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
+ guid1.register(obj)
+ guid1.register(obj.Slot(5).Equipment.get)
obj.Continent = "test"
obj.Release
@@ -573,7 +579,6 @@ class AvatarReleaseEarly1Test extends ActorTest {
expectNoMessage(100 milliseconds) //spacer
service ! Service.Join("test")
- taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMessage(200 milliseconds) //spacer
@@ -612,14 +617,19 @@ class AvatarReleaseEarly2Test extends ActorTest {
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) }
}
+ val guid1: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
+ zone.GUID(guid1)
val service = system.actorOf(Props(classOf[AvatarService], zone), "release-test-service")
- val taskResolver = system.actorOf(Props[TaskResolver](), "release-test-resolver")
zone.actor = system.spawn(ZoneActor(zone), "release-test-zone")
val objAlt =
Player(
Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 1, CharacterVoice.Voice1)
) //necessary clutter
- val obj = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
+ objAlt.GUID = PlanetSideGUID(3)
+ objAlt.Slot(5).Equipment.get.GUID = PlanetSideGUID(4)
+ val obj = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
+ guid1.register(obj)
+ guid1.register(obj.Slot(5).Equipment.get)
obj.Continent = "test"
obj.Release
@@ -628,7 +638,6 @@ class AvatarReleaseEarly2Test extends ActorTest {
expectNoMessage(100 milliseconds) //spacer
service ! Service.Join("test")
- taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMessage(200 milliseconds) //spacer
diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index a5bfcd6c..f8c14fe1 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -1,13 +1,26 @@
package net.psforever.actors.session
-import java.util.concurrent.TimeUnit
-
-import akka.actor.MDCContextAware.Implicits._
+//language imports
import akka.actor.typed
import akka.actor.typed.receptionist.Receptionist
+import akka.actor.typed.scaladsl.adapter._
import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware}
-import net.psforever.objects.{GlobalDefinitions, _}
-import net.psforever.objects.avatar.{Avatar, Certification, DeployableToolbox}
+import akka.pattern.ask
+import akka.util.Timeout
+import java.util.concurrent.TimeUnit
+import MDCContextAware.Implicits._
+import org.log4s.MDC
+import scala.collection.mutable
+import scala.concurrent.{Await, Future}
+import scala.concurrent.duration._
+import scala.concurrent.ExecutionContext.Implicits.global
+import scala.util.Success
+import scodec.bits.ByteVector
+//project imports
+import net.psforever.login.{DropCryptoSession, DropSession, HelloFriend, RawPacket}
+import net.psforever.login.WorldSession._
+import net.psforever.objects._
+import net.psforever.objects.avatar.{Avatar, Certification, Cosmetic, DeployableToolbox}
import net.psforever.objects.ballistics._
import net.psforever.objects.ce._
import net.psforever.objects.definition._
@@ -16,6 +29,7 @@ import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity}
import net.psforever.objects.equipment.{EffectTarget, Equipment, FireModeSwitch, JammableUnit}
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{Container, InventoryItem}
+import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.damage.Damageable
@@ -35,30 +49,19 @@ import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
import net.psforever.objects.serverobject.zipline.ZipLinePath
-import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.teamwork.Squad
-import net.psforever.objects.vehicles.{
- AccessPermissionGroup,
- CargoBehavior,
- MountedWeapons,
- Utility,
- UtilityType,
- VehicleControl,
- VehicleLockState
-}
+import net.psforever.objects.vehicles._
import net.psforever.objects.vehicles.Utility.InternalTelepad
import net.psforever.objects.vital._
import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
import net.psforever.packet._
import net.psforever.packet.control._
-import net.psforever.packet.game.objectcreate._
import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _}
-import net.psforever.types._
-import org.log4s.MDC
-import scodec.bits.ByteVector
+import net.psforever.packet.game.objectcreate._
import net.psforever.services.ServiceManager.LookupResult
import net.psforever.services.account.{AccountPersistenceService, PlayerToken, ReceiveAccountData, RetrieveAccountData}
import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
+import net.psforever.services.chat.ChatService
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse}
import net.psforever.services.local.support.RouterTelepadActivation
import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
@@ -72,22 +75,9 @@ import net.psforever.services.teamwork.{
}
import net.psforever.services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import net.psforever.services.{InterstellarClusterService, RemoverActor, Service, ServiceManager}
-import net.psforever.login.{DropCryptoSession, DropSession, HelloFriend, RawPacket}
+import net.psforever.types._
import net.psforever.util.{Config, DefinitionUtil}
-import net.psforever.login.WorldSession._
import net.psforever.zones.Zones
-import net.psforever.services.chat.ChatService
-import net.psforever.objects.avatar.Cosmetic
-
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.concurrent.duration._
-import scala.concurrent.{Await, Future}
-import scala.util.Success
-import akka.actor.typed.scaladsl.adapter._
-import akka.pattern.ask
-import akka.util.Timeout
-
-import scala.collection.mutable
object SessionActor {
sealed trait Command
@@ -732,7 +722,7 @@ class SessionActor extends Actor with MDCContextAware {
unk7 = 0
)
) //repeat of our entry
- val playerGuid = player.GUID
+ val playerGuid = player.GUID
//turn lfs off
val factionChannel = s"${player.Faction}"
if (avatar.lookingForSquad) {
@@ -885,7 +875,7 @@ class SessionActor extends Actor with MDCContextAware {
SquadUIElement(element.name, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
entry
case (entry, element)
- if entry.health != element.health || entry.armor != element.armor || entry.pos != element.position =>
+ if entry.health != element.health || entry.armor != element.armor || entry.pos != element.position =>
//other elements that need to be updated
squadUI(entry.char_id) =
SquadUIElement(element.name, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
@@ -1390,7 +1380,7 @@ class SessionActor extends Actor with MDCContextAware {
} else if (tplayer.isAlive) {
if (
zoneLoaded.contains(true) &&
- tplayer.HasGUID && tplayer.Actor != Default.Actor && (continent.GUID(tplayer.VehicleSeated) match {
+ tplayer.HasGUID && tplayer.Actor != Default.Actor && (continent.GUID(tplayer.VehicleSeated) match {
case Some(o: Vehicle) => o.HasGUID && o.Actor != Default.Actor && !o.Destroyed
case _ => true
})
@@ -1554,11 +1544,11 @@ class SessionActor extends Actor with MDCContextAware {
}
case msg @ Containable.ItemPutInSlot(
- _: PlanetSideServerObject with Container,
- _: Equipment,
- _: Int,
- _: Option[Equipment]
- ) =>
+ _: PlanetSideServerObject with Container,
+ _: Equipment,
+ _: Int,
+ _: Option[Equipment]
+ ) =>
log.info(s"$msg")
case msg @ Containable.CanNotPutItemInSlot(_: PlanetSideServerObject with Container, _: Equipment, _: Int) =>
@@ -1960,19 +1950,19 @@ class SessionActor extends Actor with MDCContextAware {
}
case AvatarResponse.PlayerState(
- pos,
- vel,
- yaw,
- pitch,
- yaw_upper,
- seq_time,
- is_crouching,
- is_jumping,
- jump_thrust,
- is_cloaking,
- spectating,
- weaponInHand
- ) =>
+ pos,
+ vel,
+ yaw,
+ pitch,
+ yaw_upper,
+ seq_time,
+ is_crouching,
+ is_jumping,
+ jump_thrust,
+ is_cloaking,
+ spectating,
+ weaponInHand
+ ) =>
if (tplayer_guid != guid) {
val now = System.currentTimeMillis()
val (location, time, distanceSq): (Vector3, Long, Float) = if (spectating) {
@@ -2803,18 +2793,18 @@ class SessionActor extends Actor with MDCContextAware {
}
case VehicleResponse.VehicleState(
- vehicle_guid,
- unk1,
- pos,
- ang,
- vel,
- unk2,
- unk3,
- unk4,
- wheel_direction,
- unk5,
- unk6
- ) =>
+ vehicle_guid,
+ unk1,
+ pos,
+ ang,
+ vel,
+ unk2,
+ unk3,
+ unk4,
+ wheel_direction,
+ unk5,
+ unk6
+ ) =>
if (tplayer_guid != guid) {
sendResponse(
VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6)
@@ -2948,10 +2938,10 @@ class SessionActor extends Actor with MDCContextAware {
* @return a tuple composed of an `ObjectAttachMessage` packet and a `CargoMountPointStatusMessage` packet
*/
def CargoMountBehaviorForUs(
- carrier: Vehicle,
- cargo: Vehicle,
- mountPoint: Int
- ): (ObjectAttachMessage, CargoMountPointStatusMessage) = {
+ carrier: Vehicle,
+ cargo: Vehicle,
+ mountPoint: Int
+ ): (ObjectAttachMessage, CargoMountPointStatusMessage) = {
val msgs @ (attachMessage, mountPointStatusMessage) = CargoBehavior.CargoMountMessages(carrier, cargo, mountPoint)
CargoMountMessagesForUs(attachMessage, mountPointStatusMessage)
msgs
@@ -2965,9 +2955,9 @@ class SessionActor extends Actor with MDCContextAware {
* @param mountPointStatusMessage a `CargoMountPointStatusMessage` packet suitable for initializing cargo operations
*/
def CargoMountMessagesForUs(
- attachMessage: ObjectAttachMessage,
- mountPointStatusMessage: CargoMountPointStatusMessage
- ): Unit = {
+ attachMessage: ObjectAttachMessage,
+ mountPointStatusMessage: CargoMountPointStatusMessage
+ ): Unit = {
sendResponse(attachMessage)
sendResponse(mountPointStatusMessage)
}
@@ -3329,10 +3319,10 @@ class SessionActor extends Actor with MDCContextAware {
context.system.scheduler.scheduleWithFixedDelay(0 seconds, 500 milliseconds, self, PokeClient())
accountIntermediary ! RetrieveAccountData(token)
- case msg @ MountVehicleCargoMsg(player_guid, cargo_guid, carrier_guid, unk4) =>
+ case msg@MountVehicleCargoMsg(player_guid, cargo_guid, carrier_guid, unk4) =>
log.info(msg.toString)
(continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match {
- case (Some(cargo: Vehicle), Some(carrier: Vehicle)) =>
+ case (Some(cargo : Vehicle), Some(carrier : Vehicle)) =>
carrier.CargoHolds.find({ case (_, hold) => !hold.isOccupied }) match {
case Some((mountPoint, _)) => //try begin the mount process
cargo.Actor ! CargoBehavior.CheckCargoMounting(carrier_guid, mountPoint, 0)
@@ -3348,24 +3338,24 @@ class SessionActor extends Actor with MDCContextAware {
case _ => ;
}
- case msg @ DismountVehicleCargoMsg(player_guid, cargo_guid, bailed, requestedByPassenger, kicked) =>
+ case msg@DismountVehicleCargoMsg(player_guid, cargo_guid, bailed, requestedByPassenger, kicked) =>
log.info(msg.toString)
//when kicked by carrier driver, player_guid will be PlanetSideGUID(0)
//when exiting of the cargo vehicle driver's own accord, player_guid will be the cargo vehicle driver
continent.GUID(cargo_guid) match {
- case Some(cargo: Vehicle) if !requestedByPassenger =>
+ case Some(cargo : Vehicle) if !requestedByPassenger =>
continent.GUID(cargo.MountedIn) match {
- case Some(carrier: Vehicle) =>
+ case Some(carrier : Vehicle) =>
CargoBehavior.HandleVehicleCargoDismount(continent, cargo_guid, bailed, requestedByPassenger, kicked)
case _ => ;
}
case _ => ;
}
- case msg @ CharacterCreateRequestMessage(name, head, voice, gender, empire) =>
+ case msg@CharacterCreateRequestMessage(name, head, voice, gender, empire) =>
avatarActor ! AvatarActor.CreateAvatar(name, head, voice, gender, empire)
- case msg @ CharacterRequestMessage(charId, action) =>
+ case msg@CharacterRequestMessage(charId, action) =>
action match {
case CharacterRequestAction.Delete =>
avatarActor ! AvatarActor.DeleteAvatar(charId.toInt)
@@ -3376,11 +3366,11 @@ class SessionActor extends Actor with MDCContextAware {
case KeepAliveMessage(_) =>
keepAliveFunc()
- case msg @ BeginZoningMessage() =>
+ case msg@BeginZoningMessage() =>
log.info("Reticulating splines ...")
zoneLoaded = None
- val continentId = continent.id
- val faction = player.Faction
+ val continentId = continent.id
+ val faction = player.Faction
val factionChannel = s"$faction"
continent.AvatarEvents ! Service.Join(continentId)
continent.AvatarEvents ! Service.Join(factionChannel)
@@ -3393,7 +3383,7 @@ class SessionActor extends Actor with MDCContextAware {
if (connectionState != 100) configZone(continent)
sendResponse(TimeOfDayMessage(1191182336))
//custom
- sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
+ sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks
//find and reclaim own deployables, if any
@@ -3422,7 +3412,7 @@ class SessionActor extends Actor with MDCContextAware {
)
})
turrets.foreach(obj => {
- val objGUID = obj.GUID
+ val objGUID = obj.GUID
val definition = obj.Definition
sendResponse(
ObjectCreateMessage(
@@ -3456,11 +3446,11 @@ class SessionActor extends Actor with MDCContextAware {
normal
.filter(obj =>
obj.Definition.DeployCategory == DeployableCategory.Sensors &&
- !obj.Destroyed &&
- (obj match {
- case jObj: JammableUnit => !jObj.Jammed;
- case _ => true
- })
+ !obj.Destroyed &&
+ (obj match {
+ case jObj : JammableUnit => !jObj.Jammed;
+ case _ => true
+ })
)
.foreach(obj => {
sendResponse(TriggerEffectMessage(obj.GUID, "on", true, 1000))
@@ -3527,7 +3517,7 @@ class SessionActor extends Actor with MDCContextAware {
(
a,
(continent.GUID(player.VehicleSeated) match {
- case Some(vehicle: Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
+ case Some(vehicle : Vehicle) if vehicle.PassengerInSeat(player).isDefined =>
b.partition {
_.GUID != vehicle.GUID
}
@@ -3545,7 +3535,7 @@ class SessionActor extends Actor with MDCContextAware {
val allActiveVehicles = vehicles ++ usedVehicle
//active vehicles (and some wreckage)
vehicles.foreach(vehicle => {
- val vguid = vehicle.GUID
+ val vguid = vehicle.GUID
val vdefinition = vehicle.Definition
sendResponse(
ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get)
@@ -3555,7 +3545,7 @@ class SessionActor extends Actor with MDCContextAware {
.filter({ case (index, seat) => seat.isOccupied && live.contains(seat.Occupant.get) && index > 0 })
.foreach({
case (index, seat) =>
- val targetPlayer = seat.Occupant.get
+ val targetPlayer = seat.Occupant.get
val targetDefiniton = targetPlayer.avatar.definition
sendResponse(
ObjectCreateMessage(
@@ -3583,7 +3573,7 @@ class SessionActor extends Actor with MDCContextAware {
})
.foreach({
case (index, seat) =>
- val targetPlayer = seat.Occupant.get
+ val targetPlayer = seat.Occupant.get
val targetDefinition = targetPlayer.avatar.definition
sendResponse(
ObjectCreateMessage(
@@ -3637,9 +3627,9 @@ class SessionActor extends Actor with MDCContextAware {
//special effects
sendResponse(PlanetsideAttributeMessage(obj.GUID, 52, 1)) // ant panel glow
Vehicles.FindANTChargingSource(obj, None).orElse(Vehicles.FindANTDischargingTarget(obj, None)) match {
- case Some(silo: ResourceSilo) =>
+ case Some(silo : ResourceSilo) =>
sendResponse(PlanetsideAttributeMessage(silo.GUID, 49, 1)) // silo orb particle effect
- case Some(_: WarpGate) =>
+ case Some(_ : WarpGate) =>
sendResponse(PlanetsideAttributeMessage(obj.GUID, 49, 1)) // ant orb particle effect
case _ => ;
}
@@ -3656,7 +3646,7 @@ class SessionActor extends Actor with MDCContextAware {
case ((terminal_guid, interface_guid)) =>
val parent_guid = PlanetSideGUID(terminal_guid)
continent.GUID(interface_guid) match {
- case Some(obj: Terminal) =>
+ case Some(obj : Terminal) =>
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
@@ -3670,7 +3660,7 @@ class SessionActor extends Actor with MDCContextAware {
}
//seat terminal occupants
continent.GUID(terminal_guid) match {
- case Some(obj: Mountable) =>
+ case Some(obj : Mountable) =>
obj.Seats(0).Occupant match {
case Some(targetPlayer) =>
val targetDefinition = targetPlayer.avatar.definition
@@ -3692,12 +3682,12 @@ class SessionActor extends Actor with MDCContextAware {
continent.map.turretToWeapon
.map { case ((turret_guid, _)) => continent.GUID(turret_guid) }
.collect {
- case Some(turret: FacilityTurret) =>
+ case Some(turret : FacilityTurret) =>
val pguid = turret.GUID
//attached weapon
if (!turret.isUpgrading) {
turret.ControlledWeapon(wepNumber = 1) match {
- case Some(obj: Tool) =>
+ case Some(obj : Tool) =>
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
@@ -3727,7 +3717,10 @@ class SessionActor extends Actor with MDCContextAware {
case None => ;
}
}
- continent.VehicleEvents ! VehicleServiceMessage(continent.id, VehicleAction.UpdateAmsSpawnPoint(continent))
+ continent.VehicleEvents ! VehicleServiceMessage(
+ continent.id,
+ VehicleAction.UpdateAmsSpawnPoint(continent)
+ )
upstreamMessageCount = 0
zoneLoaded = Some(true)
@@ -3750,7 +3743,7 @@ class SessionActor extends Actor with MDCContextAware {
//log.info(s"$msg")
persist()
turnCounterFunc(avatar_guid)
- val isMoving = WorldEntity.isMoving(vel)
+ val isMoving = WorldEntity.isMoving(vel)
val isMovingPlus = isMoving || is_jumping || jump_thrust
if (isMovingPlus) {
CancelZoningProcessWithDescriptiveReason("cancel_motion")
@@ -3775,7 +3768,10 @@ class SessionActor extends Actor with MDCContextAware {
}
accessedContainer match {
case Some(veh: Vehicle) =>
- if (isMoving || veh.isMoving(1) || Vector3.DistanceSquared(player.Position, veh.TrunkLocation) > 9) {
+ if (isMoving || veh.isMoving(1) || Vector3.DistanceSquared(
+ player.Position,
+ veh.TrunkLocation
+ ) > 9) {
val guid = player.GUID
sendResponse(UnuseItemMessage(guid, veh.GUID))
sendResponse(UnuseItemMessage(guid, guid))
@@ -3861,18 +3857,18 @@ class SessionActor extends Actor with MDCContextAware {
}
case msg @ VehicleStateMessage(
- vehicle_guid,
- unk1,
- pos,
- ang,
- vel,
- flying,
- unk6,
- unk7,
- wheels,
- is_decelerating,
- is_cloaked
- ) =>
+ vehicle_guid,
+ unk1,
+ pos,
+ ang,
+ vel,
+ flying,
+ unk6,
+ unk7,
+ wheels,
+ is_decelerating,
+ is_cloaked
+ ) =>
//log.info(s"$msg")
GetVehicleAndSeat() match {
case (Some(obj), Some(0)) =>
@@ -4096,7 +4092,7 @@ class SessionActor extends Actor with MDCContextAware {
//the decimator does not send a ChangeFireState_Start on the last shot
if (
tool.Definition == GlobalDefinitions.phoenix &&
- tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
+ tool.Projectile != GlobalDefinitions.phoenix_missile_guided_projectile
) {
//suppress the decimator's alternate fire mode, however
continent.AvatarEvents ! AvatarServiceMessage(
@@ -4221,12 +4217,12 @@ class SessionActor extends Actor with MDCContextAware {
}
val sumReloadValue: Int = box.Capacity + tailReloadValue
val actualReloadValue = (if (sumReloadValue <= reloadValue) {
- deleteFunc(box)
- sumReloadValue
- } else {
- modifyFunc(box, reloadValue - tailReloadValue)
- reloadValue
- }) + currentMagazine
+ deleteFunc(box)
+ sumReloadValue
+ } else {
+ modifyFunc(box, reloadValue - tailReloadValue)
+ reloadValue
+ }) + currentMagazine
log.info(s"ReloadMessage: success, $tool <- $actualReloadValue ${tool.AmmoType}")
tool.Magazine = actualReloadValue
sendResponse(ReloadMessage(item_guid, actualReloadValue, unk1))
@@ -4439,10 +4435,10 @@ class SessionActor extends Actor with MDCContextAware {
log.info(s"MoveItem: $msg")
(continent.GUID(source_guid), continent.GUID(destination_guid), ValidObject(item_guid)) match {
case (
- Some(source: PlanetSideServerObject with Container),
- Some(destination: PlanetSideServerObject with Container),
- Some(item: Equipment)
- ) =>
+ Some(source: PlanetSideServerObject with Container),
+ Some(destination: PlanetSideServerObject with Container),
+ Some(item: Equipment)
+ ) =>
source.Actor ! Containable.MoveItem(destination, item, dest)
case (None, _, _) =>
log.error(s"MoveItem: wanted to move $item_guid from $source_guid, but could not find source object")
@@ -4514,18 +4510,18 @@ class SessionActor extends Actor with MDCContextAware {
}
case msg @ UseItemMessage(
- avatar_guid,
- item_used_guid,
- object_guid,
- unk2,
- unk3,
- unk4,
- unk5,
- unk6,
- unk7,
- unk8,
- itemType
- ) =>
+ avatar_guid,
+ item_used_guid,
+ object_guid,
+ unk2,
+ unk3,
+ unk4,
+ unk5,
+ unk6,
+ unk7,
+ unk8,
+ itemType
+ ) =>
//log.info("UseItem: " + msg)
// TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok)
// TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg)
@@ -4802,8 +4798,8 @@ class SessionActor extends Actor with MDCContextAware {
//access to trunk
if (
obj.AccessingTrunk.isEmpty &&
- (!obj.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked) || obj.Owner
- .contains(player.GUID))
+ (!obj.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked) || obj.Owner
+ .contains(player.GUID))
) {
CancelZoningProcessWithDescriptiveReason("cancel_use")
obj.AccessingTrunk = player.GUID
@@ -4836,7 +4832,7 @@ class SessionActor extends Actor with MDCContextAware {
terminal.Actor ! CommonMessages.Use(player, Some(item))
case None
- if terminal.Owner == Building.NoBuilding || terminal.Faction == player.Faction || terminal.HackedBy.nonEmpty =>
+ if terminal.Owner == Building.NoBuilding || terminal.Faction == player.Faction || terminal.HackedBy.nonEmpty =>
val tdef = terminal.Definition
if (tdef.isInstanceOf[MatrixTerminalDefinition]) {
//TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
@@ -4846,7 +4842,7 @@ class SessionActor extends Actor with MDCContextAware {
)
} else if (
tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal ||
- tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal
+ tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal
) {
FindLocalVehicle match {
case Some(vehicle) =>
@@ -5215,7 +5211,6 @@ class SessionActor extends Actor with MDCContextAware {
case msg @ FavoritesRequest(player_guid, loadoutType, action, line, label) =>
CancelZoningProcessWithDescriptiveReason("cancel_use")
log.info(s"FavoritesRequest: $msg")
-
action match {
case FavoritesAction.Save => avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
case FavoritesAction.Delete => avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line)
@@ -5237,18 +5232,18 @@ class SessionActor extends Actor with MDCContextAware {
}
case msg @ WeaponFireMessage(
- seq_time,
- weapon_guid,
- projectile_guid,
- shot_origin,
- unk1,
- unk2,
- unk3,
- unk4,
- unk5,
- unk6,
- unk7
- ) =>
+ seq_time,
+ weapon_guid,
+ projectile_guid,
+ shot_origin,
+ unk1,
+ unk2,
+ unk3,
+ unk4,
+ unk5,
+ unk6,
+ unk7
+ ) =>
//log.info(s"WeaponFire: $msg")
HandleWeaponFire(weapon_guid, projectile_guid, shot_origin)
@@ -5270,7 +5265,15 @@ class SessionActor extends Actor with MDCContextAware {
case _ => ;
}
- case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
+ case msg @ HitMessage(
+ seq_time,
+ projectile_guid,
+ unk1,
+ hit_info,
+ unk2,
+ unk3,
+ unk4
+ ) =>
log.info(s"Hit: $msg")
//find defined projectile
FindProjectileEntry(projectile_guid) match {
@@ -5337,25 +5340,33 @@ class SessionActor extends Actor with MDCContextAware {
}
case msg @ SplashHitMessage(
- seq_time,
- projectile_guid,
- explosion_pos,
- direct_victim_uid,
- unk3,
- projectile_vel,
- unk4,
- targets
- ) =>
+ seq_time,
+ projectile_guid,
+ explosion_pos,
+ direct_victim_uid,
+ unk3,
+ projectile_vel,
+ unk4,
+ targets
+ ) =>
log.info(s"Splash: $msg")
FindProjectileEntry(projectile_guid) match {
case Some(projectile) =>
+ val profile = projectile.profile
projectile.Position = explosion_pos
projectile.Velocity = projectile_vel
+ val (resolution1, resolution2) = profile.Aggravated match {
+ case Some(_)
+ if profile.ProjectileDamageTypes.contains(DamageType.Aggravated) =>
+ (ProjectileResolution.AggravatedDirect, ProjectileResolution.AggravatedSplash)
+ case _ =>
+ (ProjectileResolution.Splash, ProjectileResolution.Splash)
+ }
//direct_victim_uid
ValidObject(direct_victim_uid) match {
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
- CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
- ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position) match {
+ CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
+ ResolveProjectileEntry(projectile, resolution1, target, target.Position) match {
case Some(projectile) =>
HandleDealingDamage(target, projectile)
case None => ;
@@ -5367,7 +5378,7 @@ class SessionActor extends Actor with MDCContextAware {
ValidObject(elem.uid) match {
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
- ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos) match {
+ ResolveProjectileEntry(projectile, resolution2, target, explosion_pos) match {
case Some(projectile) =>
HandleDealingDamage(target, projectile)
case None => ;
@@ -5375,7 +5386,7 @@ class SessionActor extends Actor with MDCContextAware {
case _ => ;
}
})
- if (projectile.profile.ExistsOnRemoteClients && projectile.HasGUID) {
+ if (profile.ExistsOnRemoteClients && projectile.HasGUID) {
//cleanup
val localIndex = projectile_guid.guid - Projectile.baseUID
if (projectile.HasGUID) {
@@ -5416,11 +5427,11 @@ class SessionActor extends Actor with MDCContextAware {
if (deadState != DeadState.RespawnTime) {
continent.Buildings.values.find(building => building.GUID == building_guid) match {
case Some(wg: WarpGate) if (wg.Active && (GetKnownVehicleAndSeat() match {
- case (Some(vehicle), _) =>
- wg.Definition.VehicleAllowance && !wg.Definition.NoWarp.contains(vehicle.Definition)
- case _ =>
- true
- })) =>
+ case (Some(vehicle), _) =>
+ wg.Definition.VehicleAllowance && !wg.Definition.NoWarp.contains(vehicle.Definition)
+ case _ =>
+ true
+ })) =>
deadState = DeadState.RespawnTime
cluster ! InterstellarClusterService.GetSpawnPoint(
destinationZoneGuid.guid,
@@ -5578,17 +5589,17 @@ class SessionActor extends Actor with MDCContextAware {
log.debug("Ouch! " + msg)
case msg @ BugReportMessage(
- version_major,
- version_minor,
- version_date,
- bug_type,
- repeatable,
- location,
- zone,
- pos,
- summary,
- desc
- ) =>
+ version_major,
+ version_minor,
+ version_date,
+ bug_type,
+ repeatable,
+ location,
+ zone,
+ pos,
+ summary,
+ desc
+ ) =>
log.info("BugReportMessage: " + msg)
case msg @ BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
@@ -6295,7 +6306,7 @@ class SessionActor extends Actor with MDCContextAware {
sendResponse(ObjectDetachMessage(tool.GUID, previousBox.GUID, Vector3.Zero, 0f))
sendResponse(ObjectDetachMessage(player.GUID, box.GUID, Vector3.Zero, 0f))
obj.Inventory -= x.start //remove replacement ammo from inventory
- val ammoSlotIndex = tool.FireMode.AmmoSlotIndex
+ val ammoSlotIndex = tool.FireMode.AmmoSlotIndex
tool.AmmoSlots(ammoSlotIndex).Box = box //put replacement ammo in tool
sendResponse(ObjectAttachMessage(tool.GUID, box.GUID, ammoSlotIndex))
@@ -6320,16 +6331,16 @@ class SessionActor extends Actor with MDCContextAware {
//handle inventory contents
box.Capacity = (if (sumReloadValue <= fullMagazine) {
- sumReloadValue
- } else {
- val splitReloadAmmo: Int = sumReloadValue - fullMagazine
- log.info(
- s"ChangeAmmo: taking ${originalBoxCapacity - splitReloadAmmo} from a box of ${originalBoxCapacity} $requestedAmmoType"
- )
- val boxForInventory = AmmoBox(box.Definition, splitReloadAmmo)
- taskResolver ! stowNewFunc(boxForInventory)
- fullMagazine
- })
+ sumReloadValue
+ } else {
+ val splitReloadAmmo: Int = sumReloadValue - fullMagazine
+ log.info(
+ s"ChangeAmmo: taking ${originalBoxCapacity - splitReloadAmmo} from a box of ${originalBoxCapacity} $requestedAmmoType"
+ )
+ val boxForInventory = AmmoBox(box.Definition, splitReloadAmmo)
+ taskResolver ! stowNewFunc(boxForInventory)
+ fullMagazine
+ })
sendResponse(
InventoryStateMessage(box.GUID, tool.GUID, box.Capacity)
) //should work for both players and vehicles
@@ -6443,12 +6454,12 @@ class SessionActor extends Actor with MDCContextAware {
else { xs.map(_.obj.asInstanceOf[Tool].Magazine).reduce(_ + _) }
val sumReloadValue: Int = box.Magazine + tailReloadValue
val actualReloadValue = (if (sumReloadValue <= 3) {
- RemoveOldEquipmentFromInventory(player, taskResolver)(x.obj)
- sumReloadValue
- } else {
- ModifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
- 3
- })
+ RemoveOldEquipmentFromInventory(player, taskResolver)(x.obj)
+ sumReloadValue
+ } else {
+ ModifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
+ 3
+ })
log.info(s"found $actualReloadValue more $ammoType grenades to throw")
ModifyAmmunition(player)(
tool.AmmoSlot.Box,
@@ -6470,8 +6481,8 @@ class SessionActor extends Actor with MDCContextAware {
* the second value is the slot position of the object
*/
def FindInLocalContainer(
- object_guid: PlanetSideGUID
- )(parent: PlanetSideServerObject with Container): Option[(PlanetSideServerObject with Container, Option[Int])] = {
+ object_guid: PlanetSideGUID
+ )(parent: PlanetSideServerObject with Container): Option[(PlanetSideServerObject with Container, Option[Int])] = {
val slot: Option[Int] = parent.Find(object_guid)
slot match {
case place @ Some(_) =>
@@ -6488,10 +6499,10 @@ class SessionActor extends Actor with MDCContextAware {
* @param reason a string explaining why the state can not or will not change
*/
def CanNotChangeDeployment(
- obj: PlanetSideServerObject with Deployment,
- state: DriveState.Value,
- reason: String
- ): Unit = {
+ obj: PlanetSideServerObject with Deployment,
+ state: DriveState.Value,
+ reason: String
+ ): Unit = {
val mobileShift: String = if (obj.DeploymentState != DriveState.Mobile) {
obj.DeploymentState = DriveState.Mobile
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Mobile, 0, false, Vector3.Zero))
@@ -6933,10 +6944,10 @@ class SessionActor extends Actor with MDCContextAware {
* `(None, None)`, otherwise (even if the vehicle can be determined)
*/
def GetMountableAndSeat(
- direct: Option[PlanetSideGameObject with Mountable],
- occupant: Player,
- zone: Zone
- ): (Option[PlanetSideGameObject with Mountable], Option[Int]) =
+ direct: Option[PlanetSideGameObject with Mountable],
+ occupant: Player,
+ zone: Zone
+ ): (Option[PlanetSideGameObject with Mountable], Option[Int]) =
direct.orElse(zone.GUID(occupant.VehicleSeated)) match {
case Some(obj: PlanetSideGameObject with Mountable) =>
obj.PassengerInSeat(occupant) match {
@@ -7613,11 +7624,11 @@ class SessionActor extends Actor with MDCContextAware {
* @return the projectile
*/
def ResolveProjectileEntry(
- projectile_guid: PlanetSideGUID,
- resolution: ProjectileResolution.Value,
- target: PlanetSideGameObject with FactionAffinity with Vitality,
- pos: Vector3
- ): Option[ResolvedProjectile] = {
+ projectile_guid: PlanetSideGUID,
+ resolution: ProjectileResolution.Value,
+ target: PlanetSideGameObject with FactionAffinity with Vitality,
+ pos: Vector3
+ ): Option[ResolvedProjectile] = {
FindProjectileEntry(projectile_guid) match {
case Some(projectile) =>
ResolveProjectileEntry(projectile, resolution, target, pos)
@@ -7635,12 +7646,12 @@ class SessionActor extends Actor with MDCContextAware {
* @return a copy of the projectile
*/
def ResolveProjectileEntry(
- projectile: Projectile,
- index: Int,
- resolution: ProjectileResolution.Value,
- target: PlanetSideGameObject with FactionAffinity with Vitality,
- pos: Vector3
- ): Option[ResolvedProjectile] = {
+ projectile: Projectile,
+ index: Int,
+ resolution: ProjectileResolution.Value,
+ target: PlanetSideGameObject with FactionAffinity with Vitality,
+ pos: Vector3
+ ): Option[ResolvedProjectile] = {
if (!projectiles(index).contains(projectile)) {
log.error(s"expected projectile could not be found at $index; can not resolve")
None
@@ -7656,17 +7667,31 @@ class SessionActor extends Actor with MDCContextAware {
* @return a copy of the projectile
*/
def ResolveProjectileEntry(
- projectile: Projectile,
- resolution: ProjectileResolution.Value,
- target: PlanetSideGameObject with FactionAffinity with Vitality,
- pos: Vector3
- ): Option[ResolvedProjectile] = {
+ projectile: Projectile,
+ resolution: ProjectileResolution.Value,
+ target: PlanetSideGameObject with FactionAffinity with Vitality,
+ pos: Vector3
+ ): Option[ResolvedProjectile] = {
if (projectile.isMiss) {
log.error("expected projectile was already counted as a missed shot; can not resolve any further")
None
} else {
projectile.Resolve()
- Some(ResolvedProjectile(resolution, projectile, SourceEntry(target), target.DamageModel, pos))
+ val outProjectile = if(projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)) {
+ val quality = projectile.profile.Aggravated match {
+ case Some(aggravation)
+ if aggravation.targets.exists(validation => validation.test(target)) &&
+ aggravation.info.exists(_.damage_type == AggravatedDamage.basicDamageType(resolution)) =>
+ ProjectileQuality.AggravatesTarget
+ case _ =>
+ ProjectileQuality.Normal
+ }
+ projectile.quality(quality)
+ }
+ else {
+ projectile
+ }
+ Some(ResolvedProjectile(resolution, outProjectile, SourceEntry(target), target.DamageModel, pos))
}
}
@@ -7752,11 +7777,11 @@ class SessionActor extends Actor with MDCContextAware {
* @return a `DestroyDisplayMessage` packet that is properly formatted
*/
def DestroyDisplayMessage(
- killer: SourceEntry,
- victim: SourceEntry,
- method: Int,
- unk: Int = 121
- ): DestroyDisplayMessage = {
+ killer: SourceEntry,
+ victim: SourceEntry,
+ method: Int,
+ unk: Int = 121
+ ): DestroyDisplayMessage = {
val killer_seated = killer match {
case obj: PlayerSource => obj.Seated
case _ => false
@@ -7916,9 +7941,9 @@ class SessionActor extends Actor with MDCContextAware {
* @return `true`, if the desired certification requirements are met; `false`, otherwise
*/
def ConstructionItemPermissionComparison(
- sample: Set[Certification],
- test: Set[Certification]
- ): Boolean = {
+ sample: Set[Certification],
+ test: Set[Certification]
+ ): Boolean = {
import Certification._
val engineeringCerts: Set[Certification] = Set(AssaultEngineering, FortificationEngineering)
val testDiff: Set[Certification] = test diff (engineeringCerts ++ Set(AdvancedEngineering))
@@ -8063,10 +8088,10 @@ class SessionActor extends Actor with MDCContextAware {
* `false`, otherwise
*/
def SafelyRemoveConstructionItemFromSlot(
- tool: ConstructionItem,
- index: Int,
- logDecorator: String = "SafelyRemoveConstructionItemFromSlot"
- ): Boolean = {
+ tool: ConstructionItem,
+ index: Int,
+ logDecorator: String = "SafelyRemoveConstructionItemFromSlot"
+ ): Boolean = {
if ({
val holster = player.Slot(index)
if (holster.Equipment.contains(tool)) {
@@ -8165,7 +8190,7 @@ class SessionActor extends Actor with MDCContextAware {
*/
def FindEquipmentToDelete(object_guid: PlanetSideGUID, obj: Equipment): Boolean = {
val findFunc
- : PlanetSideServerObject with Container => Option[(PlanetSideServerObject with Container, Option[Int])] =
+ : PlanetSideServerObject with Container => Option[(PlanetSideServerObject with Container, Option[Int])] =
FindInLocalContainer(object_guid)
findFunc(player.avatar.locker)
@@ -8232,12 +8257,12 @@ class SessionActor extends Actor with MDCContextAware {
* @param deletionType the value passed to `ObjectDeleteMessage` concerning the deconstruction animation
*/
def DeconstructDeployable(
- obj: PlanetSideGameObject with Deployable,
- guid: PlanetSideGUID,
- pos: Vector3,
- orient: Vector3,
- deletionType: Int
- ): Unit = {
+ obj: PlanetSideGameObject with Deployable,
+ guid: PlanetSideGUID,
+ pos: Vector3,
+ orient: Vector3,
+ deletionType: Int
+ ): Unit = {
StartBundlingPackets()
sendResponse(SetEmpireMessage(guid, PlanetSideEmpire.NEUTRAL)) //for some, removes the green marker circle
sendResponse(TriggerEffectMessage("spawn_object_failed_effect", pos, orient))
@@ -8501,17 +8526,17 @@ class SessionActor extends Actor with MDCContextAware {
player.Continent = zoneId //forward-set the continent id to perform a test
interstellarFerryTopLevelGUID =
(if (
- manifest.passengers.isEmpty && manifest.cargo.count { case (name, _) => !name.equals("MISSING_DRIVER") } == 0
- ) {
- //do not delete if vehicle has passengers or cargo
- continent.VehicleEvents ! VehicleServiceMessage(
- continent.id,
- VehicleAction.UnloadVehicle(pguid, continent, vehicle, topLevel)
- )
- None
- } else {
- Some(topLevel)
- })
+ manifest.passengers.isEmpty && manifest.cargo.count { case (name, _) => !name.equals("MISSING_DRIVER") } == 0
+ ) {
+ //do not delete if vehicle has passengers or cargo
+ continent.VehicleEvents ! VehicleServiceMessage(
+ continent.id,
+ VehicleAction.UnloadVehicle(pguid, continent, vehicle, topLevel)
+ )
+ None
+ } else {
+ Some(topLevel)
+ })
//unregister vehicle and driver whole + GiveWorld
continent.Transport ! Zone.Vehicle.Despawn(vehicle)
taskThenZoneChange(
@@ -8675,10 +8700,10 @@ class SessionActor extends Actor with MDCContextAware {
* @param remoteTelepad the endpoint of the teleportation system that exists in the environment
*/
def LinkRouterToRemoteTelepad(
- router: Vehicle,
- internalTelepad: Utility.InternalTelepad,
- remoteTelepad: TelepadDeployable
- ): Unit = {
+ router: Vehicle,
+ internalTelepad: Utility.InternalTelepad,
+ remoteTelepad: TelepadDeployable
+ ): Unit = {
internalTelepad.Telepad = remoteTelepad.GUID //necessary; backwards link to the (new) telepad
CreateRouterInternalTelepad(router, internalTelepad)
LinkRemoteTelepad(remoteTelepad.GUID)
@@ -8739,12 +8764,12 @@ class SessionActor extends Actor with MDCContextAware {
* @param dest the destination of the teleportation (where the player is going)
*/
def UseRouterTelepadSystem(
- router: Vehicle,
- internalTelepad: InternalTelepad,
- remoteTelepad: TelepadDeployable,
- src: PlanetSideGameObject with TelepadLike,
- dest: PlanetSideGameObject with TelepadLike
- ) = {
+ router: Vehicle,
+ internalTelepad: InternalTelepad,
+ remoteTelepad: TelepadDeployable,
+ src: PlanetSideGameObject with TelepadLike,
+ dest: PlanetSideGameObject with TelepadLike
+ ) = {
val time = System.nanoTime
if (
time - recentTeleportAttempt > (2 seconds).toNanos && router.DeploymentState == DriveState.Deployed && internalTelepad.Active && remoteTelepad.Active
@@ -8993,7 +9018,7 @@ class SessionActor extends Actor with MDCContextAware {
// Charge
else if (
player.Capacitor < player.ExoSuitDef.MaxCapacitor
- && (player.CapacitorState == CapacitorStateType.Idle || player.CapacitorState == CapacitorStateType.Charging || (player.CapacitorState == CapacitorStateType.ChargeDelay && System
+ && (player.CapacitorState == CapacitorStateType.Idle || player.CapacitorState == CapacitorStateType.Charging || (player.CapacitorState == CapacitorStateType.ChargeDelay && System
.currentTimeMillis() - player.CapacitorLastUsedMillis > player.ExoSuitDef.CapacitorRechargeDelayMillis))
) {
if (player.CapacitorState == CapacitorStateType.Charging) {
@@ -9127,10 +9152,10 @@ class SessionActor extends Actor with MDCContextAware {
}
def CheckForHitPositionDiscrepancy(
- projectile_guid: PlanetSideGUID,
- hitPos: Vector3,
- target: PlanetSideGameObject with FactionAffinity with Vitality
- ): Unit = {
+ projectile_guid: PlanetSideGUID,
+ hitPos: Vector3,
+ target: PlanetSideGameObject with FactionAffinity with Vitality
+ ): Unit = {
val hitPositionDiscrepancy = Vector3.DistanceSquared(hitPos, target.Position)
if (hitPositionDiscrepancy > Config.app.antiCheat.hitPositionDiscrepancyThreshold) {
// If the target position on the server does not match the position where the projectile landed within reason there may be foul play
@@ -9340,18 +9365,18 @@ class SessionActor extends Actor with MDCContextAware {
s"WeaponFireMessage: ${projectile_info.Name} is a remote projectile"
)
taskResolver ! (if (projectile.HasGUID) {
- continent.AvatarEvents ! AvatarServiceMessage(
- continent.id,
- AvatarAction.ProjectileExplodes(
- player.GUID,
- projectile.GUID,
- projectile
- )
- )
- ReregisterProjectile(projectile)
- } else {
- RegisterProjectile(projectile)
- })
+ continent.AvatarEvents ! AvatarServiceMessage(
+ continent.id,
+ AvatarAction.ProjectileExplodes(
+ player.GUID,
+ projectile.GUID,
+ projectile
+ )
+ )
+ ReregisterProjectile(projectile)
+ } else {
+ RegisterProjectile(projectile)
+ })
}
projectilesToCleanUp(projectileIndex) = false
diff --git a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
index 66c261ab..252d3e0c 100644
--- a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
+++ b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
@@ -9,7 +9,9 @@ import net.psforever.objects.definition.converter.SmallDeployableConverter
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.damage.Damageable
-import net.psforever.objects.vital.{StandardResolutions, Vitality}
+import net.psforever.objects.serverobject.damage.Damageable.Target
+import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
+import net.psforever.objects.vital.StandardResolutions
import net.psforever.objects.zones.Zone
import net.psforever.types.{PlanetSideGUID, Vector3}
import net.psforever.services.Service
@@ -63,18 +65,20 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
case _ => ;
}
- protected def TakesDamage: Receive = {
- case Vitality.Damage(applyDamageTo) =>
- if (mine.CanDamage) {
- val originalHealth = mine.Health
- val cause = applyDamageTo(mine)
- val damage = originalHealth - mine.Health
- if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
- ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
- } else {
- mine.Health = originalHealth
- }
+ override protected def PerformDamage(
+ target: Target,
+ applyDamageTo: Output
+ ): Unit = {
+ if (mine.CanDamage) {
+ val originalHealth = mine.Health
+ val cause = applyDamageTo(mine)
+ val damage = originalHealth - mine.Health
+ if (Damageable.CanDamageOrJammer(mine, damage, cause)) {
+ ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
+ } else {
+ mine.Health = originalHealth
}
+ }
}
}
diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 00f38e4e..8e1d65ff 100644
--- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -2,12 +2,13 @@
package net.psforever.objects
import net.psforever.objects.avatar.Certification
-import net.psforever.objects.ballistics.Projectiles
+import net.psforever.objects.ballistics.{AggravatedDamage, AggravatedInfo, AggravatedTiming, Projectiles}
import net.psforever.objects.ce.{DeployableCategory, DeployedItem}
import net.psforever.objects.definition._
import net.psforever.objects.definition.converter._
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
+import net.psforever.objects.serverobject.aura.Aura
import net.psforever.objects.serverobject.doors.DoorDefinition
import net.psforever.objects.serverobject.generator.GeneratorDefinition
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMechDefinition
@@ -207,6 +208,8 @@ object GlobalDefinitions {
val flail_projectile = ProjectileDefinition(Projectiles.flail_projectile)
+ val flamethrower_fire_cloud = ProjectileDefinition(Projectiles.flamethrower_projectile) //flamethrower_fire_cloud
+
val flamethrower_fireball = ProjectileDefinition(Projectiles.flamethrower_fireball)
val flamethrower_projectile = ProjectileDefinition(Projectiles.flamethrower_projectile)
@@ -2037,6 +2040,7 @@ object GlobalDefinitions {
no_projectile.Name = "none"
ProjectileDefinition.CalculateDerivedFields(no_projectile)
+ no_projectile.Modifiers = Nil
bullet_105mm_projectile.Name = "105mmbullet_projectile"
bullet_105mm_projectile.Damage0 = 150
@@ -2269,6 +2273,7 @@ object GlobalDefinitions {
ProjectileDefinition.CalculateDerivedFields(aphelion_laser_projectile)
aphelion_plasma_rocket_projectile.Name = "aphelion_plasma_rocket_projectile"
+ //has property aggravated_damage_max_factor, but it's the aphelion_plasma_cloud that performs aggravated damage
aphelion_plasma_rocket_projectile.Damage0 = 38
aphelion_plasma_rocket_projectile.Damage1 = 70
aphelion_plasma_rocket_projectile.Damage2 = 95
@@ -2313,6 +2318,14 @@ object GlobalDefinitions {
aphelion_starfire_projectile.InitialVelocity = 45
aphelion_starfire_projectile.Lifespan = 7f
aphelion_starfire_projectile.ProjectileDamageType = DamageType.Aggravated
+ aphelion_starfire_projectile.Aggravated = AggravatedDamage(
+ AggravatedInfo(DamageType.Direct, 0.25f, 250),
+ Aura.None,
+ 2000,
+ 0f,
+ true,
+ List(TargetValidation(EffectTarget.Category.Aircraft, EffectTarget.Validation.Aircraft))
+ )
aphelion_starfire_projectile.ExistsOnRemoteClients = true
aphelion_starfire_projectile.RemoteClientData = (39577, 249) //starfire_projectile data
aphelion_starfire_projectile.AutoLock = true
@@ -2352,6 +2365,7 @@ object GlobalDefinitions {
chainblade_projectile.InitialVelocity = 100
chainblade_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(chainblade_projectile)
+ chainblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
colossus_100mm_projectile.Name = "colossus_100mm_projectile"
colossus_100mm_projectile.Damage0 = 58
@@ -2437,9 +2451,24 @@ object GlobalDefinitions {
comet_projectile.DamageAtEdge = 0.45f
comet_projectile.DamageRadius = 1.0f
comet_projectile.ProjectileDamageType = DamageType.Aggravated
+ comet_projectile.Aggravated = AggravatedDamage(
+ AggravatedInfo(DamageType.Direct, 0.25f, 500), //originally, .2
+ Aura.Comet,
+ AggravatedTiming(2000, 3),
+ 10f,
+ List(
+ TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player),
+ TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.Vehicle),
+ TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret)
+ )
+ )
comet_projectile.InitialVelocity = 80
comet_projectile.Lifespan = 3.1f
ProjectileDefinition.CalculateDerivedFields(comet_projectile)
+ comet_projectile.Modifiers = List(
+ DamageModifiers.CometAggravated,
+ DamageModifiers.CometAggravatedBurn
+ )
dualcycler_projectile.Name = "dualcycler_projectile"
dualcycler_projectile.Damage0 = 18
@@ -2457,6 +2486,7 @@ object GlobalDefinitions {
dynomite_projectile.Damage1 = 175
dynomite_projectile.DamageAtEdge = 0.1f
dynomite_projectile.DamageRadius = 10f
+ dynomite_projectile.GrenadeProjectile = true
dynomite_projectile.ProjectileDamageType = DamageType.Splash
dynomite_projectile.InitialVelocity = 30
dynomite_projectile.Lifespan = 3f
@@ -2579,12 +2609,28 @@ object GlobalDefinitions {
flamethrower_fireball.Damage2 = 0
flamethrower_fireball.Damage3 = 20
flamethrower_fireball.Damage4 = 0
+ flamethrower_fireball.DamageToHealthOnly = true
flamethrower_fireball.DamageAtEdge = 0.15f
flamethrower_fireball.DamageRadius = 5f
flamethrower_fireball.ProjectileDamageType = DamageType.Aggravated
+ flamethrower_fireball.Aggravated = AggravatedDamage(
+ List(AggravatedInfo(DamageType.Direct, 0.9f, 500), AggravatedInfo(DamageType.Splash, 0.9f, 500)),
+ Aura.Fire,
+ AggravatedTiming(5000, 10),
+ 0.1f,
+ false,
+ false,
+ List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
+ )
flamethrower_fireball.InitialVelocity = 15
flamethrower_fireball.Lifespan = 1.2f
ProjectileDefinition.CalculateDerivedFields(flamethrower_fireball)
+ flamethrower_fireball.Modifiers = List(
+ DamageModifiers.InfantryAggravatedDirect,
+ DamageModifiers.InfantryAggravatedSplash,
+ DamageModifiers.RadialDegrade,
+ DamageModifiers.FireballAggravatedBurn
+ )
flamethrower_projectile.Name = "flamethrower_projectile"
flamethrower_projectile.Damage0 = 10
@@ -2592,14 +2638,29 @@ object GlobalDefinitions {
flamethrower_projectile.Damage2 = 0
flamethrower_projectile.Damage3 = 4
flamethrower_projectile.Damage4 = 0
+ flamethrower_projectile.DamageToHealthOnly = true
flamethrower_projectile.Acceleration = -5
flamethrower_projectile.AccelerationUntil = 2f
flamethrower_projectile.ProjectileDamageType = DamageType.Aggravated
+ flamethrower_projectile.Aggravated = AggravatedDamage(
+ List(AggravatedInfo(DamageType.Direct, 0.5f, 500)),
+ Aura.Fire,
+ AggravatedTiming(5000, 10),
+ 0.5f,
+ false,
+ false,
+ List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
+ )
flamethrower_projectile.DegradeDelay = 1.0f
flamethrower_projectile.DegradeMultiplier = 0.5f
flamethrower_projectile.InitialVelocity = 10
flamethrower_projectile.Lifespan = 2.0f
ProjectileDefinition.CalculateDerivedFields(flamethrower_projectile)
+ flamethrower_projectile.Modifiers = List(
+ DamageModifiers.InfantryAggravatedDirect,
+ DamageModifiers.FireballAggravatedBurn,
+ DamageModifiers.MaxDistanceCutoff
+ )
flux_cannon_apc_projectile.Name = "flux_cannon_apc_projectile"
// TODO for later, maybe : set_resource_parent flux_cannon_apc_projectile game_objects flux_cannon_thresher_projectile
@@ -2651,6 +2712,7 @@ object GlobalDefinitions {
forceblade_projectile.InitialVelocity = 100
forceblade_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(forceblade_projectile)
+ forceblade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
frag_cartridge_projectile.Name = "frag_cartridge_projectile"
// TODO for later, maybe : set_resource_parent frag_cartridge_projectile game_objects frag_grenade_projectile
@@ -2658,6 +2720,7 @@ object GlobalDefinitions {
frag_cartridge_projectile.Damage1 = 100
frag_cartridge_projectile.DamageAtEdge = 0.1f
frag_cartridge_projectile.DamageRadius = 7f
+ frag_cartridge_projectile.GrenadeProjectile = true
frag_cartridge_projectile.ProjectileDamageType = DamageType.Splash
frag_cartridge_projectile.InitialVelocity = 30
frag_cartridge_projectile.Lifespan = 15f
@@ -2670,6 +2733,7 @@ object GlobalDefinitions {
frag_cartridge_projectile_b.Damage1 = 100
frag_cartridge_projectile_b.DamageAtEdge = 0.1f
frag_cartridge_projectile_b.DamageRadius = 5f
+ frag_cartridge_projectile_b.GrenadeProjectile = true
frag_cartridge_projectile_b.ProjectileDamageType = DamageType.Splash
frag_cartridge_projectile_b.InitialVelocity = 30
frag_cartridge_projectile_b.Lifespan = 2f
@@ -2681,6 +2745,7 @@ object GlobalDefinitions {
frag_grenade_projectile.Damage1 = 100
frag_grenade_projectile.DamageAtEdge = 0.1f
frag_grenade_projectile.DamageRadius = 7f
+ frag_grenade_projectile.GrenadeProjectile = true
frag_grenade_projectile.ProjectileDamageType = DamageType.Splash
frag_grenade_projectile.InitialVelocity = 30
frag_grenade_projectile.Lifespan = 15f
@@ -2693,6 +2758,7 @@ object GlobalDefinitions {
frag_grenade_projectile_enh.Damage1 = 100
frag_grenade_projectile_enh.DamageAtEdge = 0.1f
frag_grenade_projectile_enh.DamageRadius = 7f
+ frag_grenade_projectile_enh.GrenadeProjectile = true
frag_grenade_projectile_enh.ProjectileDamageType = DamageType.Splash
frag_grenade_projectile_enh.InitialVelocity = 30
frag_grenade_projectile_enh.Lifespan = 2f
@@ -2743,6 +2809,7 @@ object GlobalDefinitions {
heavy_grenade_projectile.Damage4 = 66
heavy_grenade_projectile.DamageAtEdge = 0.1f
heavy_grenade_projectile.DamageRadius = 5f
+ heavy_grenade_projectile.GrenadeProjectile = true
heavy_grenade_projectile.ProjectileDamageType = DamageType.Splash
heavy_grenade_projectile.InitialVelocity = 75
heavy_grenade_projectile.Lifespan = 5f
@@ -2828,6 +2895,7 @@ object GlobalDefinitions {
jammer_cartridge_projectile.Damage1 = 0
jammer_cartridge_projectile.DamageAtEdge = 1.0f
jammer_cartridge_projectile.DamageRadius = 10f
+ jammer_cartridge_projectile.GrenadeProjectile = true
jammer_cartridge_projectile.ProjectileDamageType = DamageType.Splash
jammer_cartridge_projectile.InitialVelocity = 30
jammer_cartridge_projectile.Lifespan = 15f
@@ -2858,7 +2926,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile)
- jammer_cartridge_projectile.Modifiers = Nil
+ jammer_cartridge_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
jammer_cartridge_projectile_b.Name = "jammer_cartridge_projectile_b"
// TODO for later, maybe : set_resource_parent jammer_cartridge_projectile_b game_objects jammer_grenade_projectile_enh
@@ -2866,6 +2934,7 @@ object GlobalDefinitions {
jammer_cartridge_projectile_b.Damage1 = 0
jammer_cartridge_projectile_b.DamageAtEdge = 1.0f
jammer_cartridge_projectile_b.DamageRadius = 10f
+ jammer_cartridge_projectile_b.GrenadeProjectile = true
jammer_cartridge_projectile_b.ProjectileDamageType = DamageType.Splash
jammer_cartridge_projectile_b.InitialVelocity = 30
jammer_cartridge_projectile_b.Lifespan = 2f
@@ -2896,13 +2965,14 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_cartridge_projectile_b)
- jammer_cartridge_projectile_b.Modifiers = Nil
+ jammer_cartridge_projectile_b.Modifiers = DamageModifiers.MaxDistanceCutoff
jammer_grenade_projectile.Name = "jammer_grenade_projectile"
jammer_grenade_projectile.Damage0 = 0
jammer_grenade_projectile.Damage1 = 0
jammer_grenade_projectile.DamageAtEdge = 1.0f
jammer_grenade_projectile.DamageRadius = 10f
+ jammer_grenade_projectile.GrenadeProjectile = true
jammer_grenade_projectile.ProjectileDamageType = DamageType.Splash
jammer_grenade_projectile.InitialVelocity = 30
jammer_grenade_projectile.Lifespan = 15f
@@ -2933,7 +3003,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile)
- jammer_grenade_projectile.Modifiers = Nil
+ jammer_grenade_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
jammer_grenade_projectile_enh.Name = "jammer_grenade_projectile_enh"
// TODO for later, maybe : set_resource_parent jammer_grenade_projectile_enh game_objects jammer_grenade_projectile
@@ -2941,6 +3011,7 @@ object GlobalDefinitions {
jammer_grenade_projectile_enh.Damage1 = 0
jammer_grenade_projectile_enh.DamageAtEdge = 1.0f
jammer_grenade_projectile_enh.DamageRadius = 10f
+ jammer_grenade_projectile_enh.GrenadeProjectile = true
jammer_grenade_projectile_enh.ProjectileDamageType = DamageType.Splash
jammer_grenade_projectile_enh.InitialVelocity = 30
jammer_grenade_projectile_enh.Lifespan = 3f
@@ -2971,7 +3042,7 @@ object GlobalDefinitions {
EffectTarget.Validation.VehicleNotAMS
) -> 10000
ProjectileDefinition.CalculateDerivedFields(jammer_grenade_projectile_enh)
- jammer_grenade_projectile_enh.Modifiers = Nil
+ jammer_grenade_projectile_enh.Modifiers = DamageModifiers.MaxDistanceCutoff
katana_projectile.Name = "katana_projectile"
katana_projectile.Damage0 = 25
@@ -3014,7 +3085,10 @@ object GlobalDefinitions {
lasher_projectile.LashRadius = 2.5f
lasher_projectile.Lifespan = 0.75f
ProjectileDefinition.CalculateDerivedFields(lasher_projectile)
- lasher_projectile.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash)
+ lasher_projectile.Modifiers = List(
+ DamageModifiers.DistanceDegrade,
+ DamageModifiers.Lash
+ )
lasher_projectile_ap.Name = "lasher_projectile_ap"
lasher_projectile_ap.Damage0 = 12
@@ -3029,7 +3103,10 @@ object GlobalDefinitions {
lasher_projectile_ap.LashRadius = 2.5f
lasher_projectile_ap.Lifespan = 0.75f
ProjectileDefinition.CalculateDerivedFields(lasher_projectile_ap)
- lasher_projectile_ap.Modifiers = List(DamageModifiers.DistanceDegrade, DamageModifiers.Lash)
+ lasher_projectile_ap.Modifiers = List(
+ DamageModifiers.DistanceDegrade,
+ DamageModifiers.Lash
+ )
liberator_bomb_cluster_bomblet_projectile.Name = "liberator_bomb_cluster_bomblet_projectile"
liberator_bomb_cluster_bomblet_projectile.Damage0 = 75
@@ -3071,6 +3148,7 @@ object GlobalDefinitions {
maelstrom_grenade_projectile.Damage1 = 60
maelstrom_grenade_projectile.DamageRadius = 20f
maelstrom_grenade_projectile.LashRadius = 5f
+ maelstrom_grenade_projectile.GrenadeProjectile = true
maelstrom_grenade_projectile.ProjectileDamageType = DamageType.Direct
maelstrom_grenade_projectile.InitialVelocity = 30
maelstrom_grenade_projectile.Lifespan = 2f
@@ -3084,6 +3162,7 @@ object GlobalDefinitions {
maelstrom_grenade_projectile_contact.Damage1 = 60
maelstrom_grenade_projectile_contact.DamageRadius = 20f
maelstrom_grenade_projectile_contact.LashRadius = 5f
+ maelstrom_grenade_projectile_contact.GrenadeProjectile = true
maelstrom_grenade_projectile_contact.ProjectileDamageType = DamageType.Direct
maelstrom_grenade_projectile_contact.InitialVelocity = 30
maelstrom_grenade_projectile_contact.Lifespan = 15f
@@ -3100,6 +3179,7 @@ object GlobalDefinitions {
maelstrom_stream_projectile.InitialVelocity = 200
maelstrom_stream_projectile.Lifespan = 0.2f
ProjectileDefinition.CalculateDerivedFields(maelstrom_stream_projectile)
+ maelstrom_stream_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
magcutter_projectile.Name = "magcutter_projectile"
// TODO for later, maybe : set_resource_parent magcutter_projectile game_objects melee_ammo_projectile
@@ -3109,6 +3189,7 @@ object GlobalDefinitions {
magcutter_projectile.InitialVelocity = 100
magcutter_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(magcutter_projectile)
+ magcutter_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
melee_ammo_projectile.Name = "melee_ammo_projectile"
melee_ammo_projectile.Damage0 = 25
@@ -3117,6 +3198,7 @@ object GlobalDefinitions {
melee_ammo_projectile.InitialVelocity = 100
melee_ammo_projectile.Lifespan = .02f
ProjectileDefinition.CalculateDerivedFields(melee_ammo_projectile)
+ melee_ammo_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
meteor_common.Name = "meteor_common"
meteor_common.DamageAtEdge = .1f
@@ -3208,6 +3290,7 @@ object GlobalDefinitions {
mine_sweeper_projectile.Damage1 = 0
mine_sweeper_projectile.DamageAtEdge = .33f
mine_sweeper_projectile.DamageRadius = 25f
+ mine_sweeper_projectile.GrenadeProjectile = true
mine_sweeper_projectile.ProjectileDamageType = DamageType.Splash
mine_sweeper_projectile.InitialVelocity = 30
mine_sweeper_projectile.Lifespan = 15f
@@ -3219,6 +3302,7 @@ object GlobalDefinitions {
mine_sweeper_projectile_enh.Damage1 = 0
mine_sweeper_projectile_enh.DamageAtEdge = 0.33f
mine_sweeper_projectile_enh.DamageRadius = 25f
+ mine_sweeper_projectile_enh.GrenadeProjectile = true
mine_sweeper_projectile_enh.ProjectileDamageType = DamageType.Splash
mine_sweeper_projectile_enh.InitialVelocity = 30
mine_sweeper_projectile_enh.Lifespan = 3f
@@ -3422,10 +3506,27 @@ object GlobalDefinitions {
plasma_cartridge_projectile.Damage1 = 15
plasma_cartridge_projectile.DamageAtEdge = 0.2f
plasma_cartridge_projectile.DamageRadius = 7f
+ plasma_cartridge_projectile.GrenadeProjectile = true
plasma_cartridge_projectile.ProjectileDamageType = DamageType.Aggravated
+ plasma_cartridge_projectile.Aggravated = AggravatedDamage(
+ List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
+ Aura.Plasma,
+ AggravatedTiming(3000),
+ 1.5f,
+ true,
+ false,
+ List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
+ )
plasma_cartridge_projectile.InitialVelocity = 30
plasma_cartridge_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile)
+ plasma_cartridge_projectile.Modifiers = List(
+ DamageModifiers.InfantryAggravatedDirect,
+ DamageModifiers.InfantryAggravatedDirectBurn,
+ DamageModifiers.InfantryAggravatedSplash,
+ DamageModifiers.InfantryAggravatedSplashBurn,
+ DamageModifiers.RadialDegrade
+ )
plasma_cartridge_projectile_b.Name = "plasma_cartridge_projectile_b"
// TODO for later, maybe : set_resource_parent plasma_cartridge_projectile_b game_objects plasma_grenade_projectile_B
@@ -3433,20 +3534,54 @@ object GlobalDefinitions {
plasma_cartridge_projectile_b.Damage1 = 15
plasma_cartridge_projectile_b.DamageAtEdge = 0.2f
plasma_cartridge_projectile_b.DamageRadius = 7f
+ plasma_cartridge_projectile_b.GrenadeProjectile = true
plasma_cartridge_projectile_b.ProjectileDamageType = DamageType.Aggravated
+ plasma_cartridge_projectile_b.Aggravated = AggravatedDamage(
+ List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
+ Aura.Plasma,
+ AggravatedTiming(3000),
+ 1.5f,
+ true,
+ false,
+ List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
+ )
plasma_cartridge_projectile_b.InitialVelocity = 30
plasma_cartridge_projectile_b.Lifespan = 2f
ProjectileDefinition.CalculateDerivedFields(plasma_cartridge_projectile_b)
+ plasma_cartridge_projectile_b.Modifiers = List(
+ DamageModifiers.InfantryAggravatedDirect,
+ DamageModifiers.InfantryAggravatedDirectBurn,
+ DamageModifiers.InfantryAggravatedSplash,
+ DamageModifiers.InfantryAggravatedSplashBurn,
+ DamageModifiers.RadialDegrade
+ )
plasma_grenade_projectile.Name = "plasma_grenade_projectile"
plasma_grenade_projectile.Damage0 = 40
plasma_grenade_projectile.Damage1 = 30
plasma_grenade_projectile.DamageAtEdge = 0.1f
plasma_grenade_projectile.DamageRadius = 7f
+ plasma_grenade_projectile.GrenadeProjectile = true
plasma_grenade_projectile.ProjectileDamageType = DamageType.Aggravated
+ plasma_grenade_projectile.Aggravated = AggravatedDamage(
+ List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
+ Aura.Plasma,
+ AggravatedTiming(3000),
+ 1.5f,
+ true,
+ false,
+ List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
+ )
plasma_grenade_projectile.InitialVelocity = 30
plasma_grenade_projectile.Lifespan = 15f
ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile)
+ plasma_grenade_projectile.Modifiers = List(
+ DamageModifiers.InfantryAggravatedDirect,
+ DamageModifiers.InfantryAggravatedDirectBurn,
+ DamageModifiers.InfantryAggravatedSplash,
+ DamageModifiers.InfantryAggravatedSplashBurn,
+ DamageModifiers.RadialDegrade
+ )
plasma_grenade_projectile_B.Name = "plasma_grenade_projectile_B"
// TODO for later, maybe : set_resource_parent plasma_grenade_projectile_B game_objects plasma_grenade_projectile
@@ -3454,10 +3589,27 @@ object GlobalDefinitions {
plasma_grenade_projectile_B.Damage1 = 30
plasma_grenade_projectile_B.DamageAtEdge = 0.1f
plasma_grenade_projectile_B.DamageRadius = 7f
+ plasma_grenade_projectile_B.GrenadeProjectile = true
plasma_grenade_projectile_B.ProjectileDamageType = DamageType.Aggravated
+ plasma_grenade_projectile_B.Aggravated = AggravatedDamage(
+ List(AggravatedInfo(DamageType.Direct, 0.25f, 750), AggravatedInfo(DamageType.Splash, 0.25f, 1000)),
+ Aura.Plasma,
+ AggravatedTiming(3000),
+ 1.5f,
+ true,
+ false,
+ List(TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player))
+ )
plasma_grenade_projectile_B.InitialVelocity = 30
plasma_grenade_projectile_B.Lifespan = 3f
ProjectileDefinition.CalculateDerivedFields(plasma_grenade_projectile_B)
+ plasma_grenade_projectile_B.Modifiers = List(
+ DamageModifiers.InfantryAggravatedDirect,
+ DamageModifiers.InfantryAggravatedDirectBurn,
+ DamageModifiers.InfantryAggravatedSplash,
+ DamageModifiers.InfantryAggravatedSplashBurn,
+ DamageModifiers.RadialDegrade
+ )
pounder_projectile.Name = "pounder_projectile"
pounder_projectile.Damage0 = 31
@@ -3467,6 +3619,7 @@ object GlobalDefinitions {
pounder_projectile.Damage4 = 132
pounder_projectile.DamageAtEdge = 0.1f
pounder_projectile.DamageRadius = 1f
+ pounder_projectile.GrenadeProjectile = true
pounder_projectile.ProjectileDamageType = DamageType.Splash
pounder_projectile.InitialVelocity = 120
pounder_projectile.Lifespan = 2.5f
@@ -3482,6 +3635,7 @@ object GlobalDefinitions {
pounder_projectile_enh.Damage4 = 132
pounder_projectile_enh.DamageAtEdge = 0.1f
pounder_projectile_enh.DamageRadius = 1f
+ pounder_projectile_enh.GrenadeProjectile = true
pounder_projectile_enh.ProjectileDamageType = DamageType.Splash
pounder_projectile_enh.InitialVelocity = 120
pounder_projectile_enh.Lifespan = 3.2f
@@ -3533,6 +3687,7 @@ object GlobalDefinitions {
ProjectileDefinition.CalculateDerivedFields(quasar_projectile)
radiator_grenade_projectile.Name = "radiator_grenade_projectile" // Todo : Radiator damages ?
+ radiator_grenade_projectile.GrenadeProjectile = true //not really, but technically yes
radiator_grenade_projectile.ProjectileDamageType = DamageType.Direct
radiator_grenade_projectile.InitialVelocity = 30
radiator_grenade_projectile.Lifespan = 3f
@@ -3540,6 +3695,7 @@ object GlobalDefinitions {
radiator_sticky_projectile.Name = "radiator_sticky_projectile"
// TODO for later, maybe : set_resource_parent radiator_sticky_projectile game_objects radiator_grenade_projectile
+ radiator_sticky_projectile.GrenadeProjectile = true //not really, but technically yes
radiator_sticky_projectile.ProjectileDamageType = DamageType.Direct
radiator_sticky_projectile.InitialVelocity = 30
radiator_sticky_projectile.Lifespan = 4f
@@ -3602,7 +3758,7 @@ object GlobalDefinitions {
rocklet_jammer_projectile.InitialVelocity = 50
rocklet_jammer_projectile.Lifespan = 8f
ProjectileDefinition.CalculateDerivedFields(rocklet_jammer_projectile)
- rocklet_jammer_projectile.Modifiers = Nil
+ //TODO rocklet_jammer_projectile.Modifiers = DamageModifiers.RadialDegrade?
scattercannon_projectile.Name = "scattercannon_projectile"
scattercannon_projectile.Damage0 = 11
@@ -3722,7 +3878,6 @@ object GlobalDefinitions {
spiker_projectile.InitialVelocity = 40
spiker_projectile.Lifespan = 5f
ProjectileDefinition.CalculateDerivedFields(spiker_projectile)
- spiker_projectile.Modifiers = DamageModifiers.RadialDegrade
spitfire_aa_ammo_projectile.Name = "spitfire_aa_ammo_projectile"
spitfire_aa_ammo_projectile.Damage0 = 5
@@ -3756,6 +3911,18 @@ object GlobalDefinitions {
starfire_projectile.Acceleration = 12
starfire_projectile.AccelerationUntil = 5f
starfire_projectile.ProjectileDamageType = DamageType.Aggravated
+ starfire_projectile.Aggravated = AggravatedDamage(
+ AggravatedInfo(DamageType.Direct, 0.25f, 250),
+ Aura.Comet,
+ 2000,
+ 0f,
+ true,
+ List(
+ TargetValidation(EffectTarget.Category.Player, EffectTarget.Validation.Player),
+ TargetValidation(EffectTarget.Category.Vehicle, EffectTarget.Validation.Vehicle),
+ TargetValidation(EffectTarget.Category.Turret, EffectTarget.Validation.Turret)
+ )
+ )
starfire_projectile.InitialVelocity = 45
starfire_projectile.Lifespan = 7.8f
starfire_projectile.ExistsOnRemoteClients = true
@@ -3763,6 +3930,10 @@ object GlobalDefinitions {
starfire_projectile.AutoLock = true
starfire_projectile.Packet = projectileConverter
ProjectileDefinition.CalculateDerivedFields(starfire_projectile)
+ starfire_projectile.Modifiers = List(
+ DamageModifiers.StarfireAggravatedBurn,
+ DamageModifiers.RadialDegrade
+ )
striker_missile_projectile.Name = "striker_missile_projectile"
striker_missile_projectile.Damage0 = 35
@@ -3813,6 +3984,7 @@ object GlobalDefinitions {
trek_projectile.InitialVelocity = 40
trek_projectile.Lifespan = 7f
ProjectileDefinition.CalculateDerivedFields(trek_projectile)
+ trek_projectile.Modifiers = DamageModifiers.MaxDistanceCutoff
vanu_sentry_turret_projectile.Name = "vanu_sentry_turret_projectile"
vanu_sentry_turret_projectile.Damage0 = 25
@@ -4491,7 +4663,7 @@ object GlobalDefinitions {
flamethrower.FireModes(1).AmmoSlotIndex = 0
flamethrower.FireModes(1).Magazine = 100
flamethrower.FireModes(1).RoundsPerShot = 50
- flamethrower.Tile = InventoryTile.Tile63
+ flamethrower.Tile = InventoryTile.Tile93
winchester.Name = "winchester"
winchester.Size = EquipmentSize.Rifle
diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala
index bd79347f..d7585f01 100644
--- a/src/main/scala/net/psforever/objects/Player.scala
+++ b/src/main/scala/net/psforever/objects/Player.scala
@@ -1,12 +1,20 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
-import net.psforever.objects.avatar.{Avatar, LoadoutManager}
-import net.psforever.objects.definition.{AvatarDefinition, ExoSuitDefinition, SpecialExoSuitDefinition}
+import net.psforever.objects.avatar.{
+ Avatar,
+ LoadoutManager
+}
+import net.psforever.objects.definition.{
+ AvatarDefinition,
+ ExoSuitDefinition,
+ SpecialExoSuitDefinition
+}
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.serverobject.aura.AuraContainer
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.objects.vital.{DamageResistanceModel, Vitality}
import net.psforever.objects.zones.ZoneAware
@@ -22,7 +30,8 @@ class Player(var avatar: Avatar)
with ResistanceProfile
with Container
with JammableUnit
- with ZoneAware {
+ with ZoneAware
+ with AuraContainer {
Health = 0 //player health is artificially managed as a part of their lifecycle; start entity as dead
Destroyed = true //see isAlive
private var backpack: Boolean = false
diff --git a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
index feb1f129..75a375ec 100644
--- a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
+++ b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
@@ -44,7 +44,6 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
def JammableObject = gen
def DamageableObject = gen
def RepairableObject = gen
- private var handleDamageToShields: Boolean = false
def receive: Receive =
jammableBehavior
@@ -90,18 +89,20 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
target,
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
)
- handleDamageToShields = damageToShields > 0
- HandleDamage(target, cause, damageToHealth)
+ HandleDamage(target, cause, (damageToHealth, damageToShields))
} else {
gen.Health = originalHealth
gen.Shields = originalShields
}
}
- override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
- super.DamageAwareness(target, cause, amount)
- ShieldGeneratorControl.DamageAwareness(gen, cause, handleDamageToShields)
- handleDamageToShields = false
+ override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ val (damageToHealth, damageToShields) = amount match {
+ case (a: Int, b: Int) => (a, b)
+ case _ => (0, 0)
+ }
+ super.DamageAwareness(target, cause, damageToHealth)
+ ShieldGeneratorControl.DamageAwareness(gen, cause, damageToShields > 0)
}
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala
index 2c3b5e8d..fd9f8569 100644
--- a/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -75,6 +75,11 @@ class TurretControl(turret: TurretDeployable)
def DamageableObject = turret
def RepairableObject = turret
+ override def postStop(): Unit = {
+ super.postStop()
+ damageableWeaponTurretPostStop()
+ }
+
def receive: Receive =
checkBehavior
.orElse(jammableBehavior)
diff --git a/src/main/scala/net/psforever/objects/Vehicle.scala b/src/main/scala/net/psforever/objects/Vehicle.scala
index d108fad3..b1a47202 100644
--- a/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -7,6 +7,7 @@ import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem,
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.serverobject.aura.AuraContainer
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.structures.AmenityOwner
@@ -78,7 +79,8 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
with StandardResistanceProfile
with JammableUnit
with CommonNtuContainer
- with Container {
+ with Container
+ with AuraContainer {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var shields: Int = 0
private var decal: Int = 0
diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index 391bdf56..b47f3338 100644
--- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -8,14 +8,17 @@ import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile}
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.loadouts.Loadout
+import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
-import net.psforever.objects.vital.{PlayerSuicide, Vitality}
+import net.psforever.objects.serverobject.damage.Damageable.Target
+import net.psforever.objects.vital.PlayerSuicide
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
-import net.psforever.objects.serverobject.damage.Damageable
+import net.psforever.objects.serverobject.damage.{AggravatedBehavior, Damageable}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.repair.Repairable
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vital._
+import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
@@ -30,13 +33,24 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
extends Actor
with JammableBehavior
with Damageable
- with ContainableBehavior {
- def JammableObject = player
+ with ContainableBehavior
+ with AggravatedBehavior
+ with AuraEffectBehavior {
+
+ def JammableObject = player
def DamageableObject = player
def ContainerObject = player
+ def AggravatedObject = player
+ ApplicableEffect(Aura.Plasma)
+ ApplicableEffect(Aura.Napalm)
+ ApplicableEffect(Aura.Comet)
+ ApplicableEffect(Aura.Fire)
+
+ def AuraTargetObject = player
+
private[this] val log = org.log4s.getLogger(player.Name)
private[this] val damageLog = org.log4s.getLogger(Damageable.LogChannel)
@@ -53,11 +67,15 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
override def postStop(): Unit = {
lockerControlAgent ! akka.actor.PoisonPill
player.avatar.locker.Actor = Default.Actor
+ EndAllEffects()
+ EndAllAggravation()
}
def receive: Receive =
jammableBehavior
.orElse(takesDamage)
+ .orElse(aggravatedBehavior)
+ .orElse(auraBehavior)
.orElse(containerBehavior)
.orElse {
case Player.Die() =>
@@ -480,29 +498,31 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
case _ => ;
}
- protected def TakesDamage: Receive = {
- case Vitality.Damage(applyDamageTo) =>
- if (player.isAlive && !player.spectator) {
- val originalHealth = player.Health
- val originalArmor = player.Armor
- val originalStamina = player.avatar.stamina
- val originalCapacitor = player.Capacitor.toInt
- val cause = applyDamageTo(player)
- val health = player.Health
- val armor = player.Armor
- val stamina = player.avatar.stamina
- val capacitor = player.Capacitor.toInt
- val damageToHealth = originalHealth - health
- val damageToArmor = originalArmor - armor
- val damageToStamina = originalStamina - stamina
- val damageToCapacitor = originalCapacitor - capacitor
- HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
- if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
- damageLog.info(
- s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor"
- )
- }
+ override protected def PerformDamage(
+ target: Target,
+ applyDamageTo: Output
+ ): Unit = {
+ if (player.isAlive && !player.spectator) {
+ val originalHealth = player.Health
+ val originalArmor = player.Armor
+ val originalStamina = player.avatar.stamina
+ val originalCapacitor = player.Capacitor.toInt
+ val cause = applyDamageTo(player)
+ val health = player.Health
+ val armor = player.Armor
+ val stamina = player.avatar.stamina
+ val capacitor = player.Capacitor.toInt
+ val damageToHealth = originalHealth - health
+ val damageToArmor = originalArmor - armor
+ val damageToStamina = originalStamina - stamina
+ val damageToCapacitor = originalCapacitor - capacitor
+ HandleDamage(player, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
+ if (damageToHealth > 0 || damageToArmor > 0 || damageToStamina > 0 || damageToCapacitor > 0) {
+ damageLog.info(
+ s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalStamina/$originalCapacitor, AFTER=$health/$armor/$stamina/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToStamina/$damageToCapacitor"
+ )
}
+ }
}
/**
@@ -510,70 +530,110 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
* @param target na
*/
def HandleDamage(
- target: Player,
- cause: ResolvedProjectile,
- damageToHealth: Int,
- damageToArmor: Int,
- damageToStamina: Int,
- damageToCapacitor: Int
- ): Unit = {
- val targetGUID = target.GUID
- val zone = target.Zone
- val zoneId = zone.id
- val events = zone.AvatarEvents
- val health = target.Health
+ target: Player,
+ cause: ResolvedProjectile,
+ damageToHealth: Int,
+ damageToArmor: Int,
+ damageToStamina: Int,
+ damageToCapacitor: Int
+ ): Unit = {
+ //always do armor update
if (damageToArmor > 0) {
- events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 4, target.Armor))
+ val zone = target.Zone
+ zone.AvatarEvents ! AvatarServiceMessage(
+ zone.id,
+ AvatarAction.PlanetsideAttributeToAll(target.GUID, 4, target.Armor)
+ )
}
- if (health > 0) {
- if (damageToCapacitor > 0) {
- events ! AvatarServiceMessage(
- target.Name,
- AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)
- )
- }
- if (damageToHealth > 0 || damageToStamina > 0) {
- target.History(cause)
- if (damageToHealth > 0) {
- events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
- }
- if (damageToStamina > 0) {
- avatarActor ! AvatarActor.ConsumeStamina(damageToStamina)
- }
- //activity on map
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
- //alert damage source
- DamageAwareness(target, cause)
- }
- if (Damageable.CanJammer(target, cause)) {
- target.Actor ! JammableUnit.Jammered(cause)
- }
+ //choose
+ if (target.Health > 0) {
+ DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
} else {
DestructionAwareness(target, Some(cause))
}
}
- /**
- * na
- * @param target na
- * @param cause na
- */
- def DamageAwareness(target: Player, cause: ResolvedProjectile): Unit = {
- val zone = target.Zone
- zone.AvatarEvents ! AvatarServiceMessage(
- target.Name,
- cause.projectile.owner match {
- case pSource: PlayerSource => //player damage
- val name = pSource.Name
- zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
- case Some(tplayer) => AvatarAction.HitHint(tplayer.GUID, target.GUID)
- case None =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
+ def DamageAwareness(
+ target: Player,
+ cause: ResolvedProjectile,
+ damageToHealth: Int,
+ damageToArmor: Int,
+ damageToStamina: Int,
+ damageToCapacitor: Int
+ ): Unit = {
+ val targetGUID = target.GUID
+ val zone = target.Zone
+ val zoneId = zone.id
+ val events = zone.AvatarEvents
+ val health = target.Health
+ var announceConfrontation = damageToArmor > 0
+ //special effects
+ if (Damageable.CanJammer(target, cause)) {
+ TryJammerEffectActivate(target, cause)
+ }
+ val aggravated: Boolean = TryAggravationEffectActivate(cause) match {
+ case Some(aggravation) =>
+ StartAuraEffect(aggravation.effect_type, aggravation.timing.duration)
+ announceConfrontation = true //useful if initial damage (to anything) is zero
+ //initial damage for aggravation, but never treat as "aggravated"
+ false
+ case _ =>
+ cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ }
+ //log historical event
+ target.History(cause)
+ //stat changes
+ if (damageToCapacitor > 0) {
+ events ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong)
+ )
+ announceConfrontation = true
+ }
+ if (damageToStamina > 0) {
+ avatarActor ! AvatarActor.ConsumeStamina(damageToStamina)
+ announceConfrontation = true
+ }
+ if (damageToHealth > 0) {
+ events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health))
+ announceConfrontation = true
+ }
+ val countableDamage = damageToHealth + damageToArmor
+ if(announceConfrontation) {
+ if (!aggravated) {
+ //activity on map
+ zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ //alert to damage source
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ cause.projectile.owner match {
+ case pSource: PlayerSource => //player damage
+ val name = pSource.Name
+ zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
+ case Some(tplayer) =>
+ AvatarAction.HitHint(tplayer.GUID, target.GUID)
+ case None =>
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
+ }
+ case source =>
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
}
- case source =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position))
+ )
}
- )
+ else {
+ //general alert
+ zone.AvatarEvents ! AvatarServiceMessage(
+ target.Name,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, Vector3.Zero))
+ )
+ }
+ }
+ if (aggravated) {
+ events ! AvatarServiceMessage(
+ zoneId,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, AggravatedDamageMessage(targetGUID, countableDamage))
+ )
+ }
}
/**
@@ -600,6 +660,10 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
val nameChannel = target.Name
val zoneChannel = zone.id
target.Die
+ //aura effects cancel
+ EndAllEffects()
+ //aggravation cancel
+ EndAllAggravation()
//unjam
CancelJammeredSound(target)
CancelJammeredStatus(target)
@@ -864,4 +928,28 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)
)
}
+
+ def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit = {
+ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
+ val zone = target.Zone
+ val value = target.Aura.foldLeft(0)(_ + PlayerControl.auraEffectToAttributeValue(_))
+ zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value))
+ }
+}
+
+object PlayerControl {
+ /**
+ * Transform an applicable Aura effect into its `PlanetsideAttributeMessage` value.
+ * @see `Aura`
+ * @see `PlanetsideAttributeMessage`
+ * @param effect the aura effect
+ * @return the attribute value for that effect
+ */
+ private def auraEffectToAttributeValue(effect: Aura): Int = effect match {
+ case Aura.Plasma => 1
+ case Aura.Comet => 2
+ case Aura.Napalm => 4
+ case Aura.Fire => 8
+ case _ => 0
+ }
}
diff --git a/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala b/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
new file mode 100644
index 00000000..91c9c3c2
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/ballistics/AggravatedDamage.scala
@@ -0,0 +1,178 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.ballistics
+import net.psforever.objects.equipment.TargetValidation
+import net.psforever.objects.serverobject.aura.Aura
+import net.psforever.objects.vital.DamageType
+
+/**
+ * In what manner of pacing the aggravated damage ticks are applied.
+ * @param duration for how long the over-all effect is applied
+ * @param ticks a custom number of damage applications,
+ * as opposed to whatever calculations normally estimate the number of applications
+ */
+final case class AggravatedTiming(duration: Long, ticks: Option[Int])
+
+object AggravatedTiming {
+ /**
+ * Overloaded constructor that only defines the duration.
+ * @param duration for how long the over-all effect lasts
+ * @return an `AggravatedTiming` object
+ */
+ def apply(duration: Long): AggravatedTiming = AggravatedTiming(duration, None)
+
+ /**
+ * Overloaded constructor.
+ * @param duration for how long the over-all effect lasts
+ * @param ticks a custom number of damage applications
+ * @return an `AggravatedTiming` object
+ */
+ def apply(duration: Long, ticks: Int): AggravatedTiming = AggravatedTiming(duration, Some(ticks))
+}
+
+/**
+ * Aggravation damage has components that are mainly divided by the `DamageType` they inflict.
+ * Only `Direct` and `Splash` are valid damage types, however.
+ * @param damage_type the type of damage
+ * @param degradation_percentage by how much the damage is degraded
+ * @param infliction_rate how often the damage is inflicted (ms)
+ */
+final case class AggravatedInfo(damage_type: DamageType.Value,
+ degradation_percentage: Float,
+ infliction_rate: Long) {
+ assert(damage_type == DamageType.Direct || damage_type == DamageType.Splash, s"aggravated damage is an unsupported type - $damage_type")
+}
+
+/**
+ * Information related to the aggravated damage.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param timing the timing for the damage application
+ * @param max_factor na (if the target is a mechanized assault exo-suit?)
+ * @param cumulative_damage_degrade na (can multiple instances of this type of aggravated damage apply to the same target at once?)
+ * @param vanu_aggravated na (search me)
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
+final case class AggravatedDamage(info: List[AggravatedInfo],
+ effect_type: Aura,
+ timing: AggravatedTiming,
+ max_factor: Float,
+ cumulative_damage_degrade: Boolean,
+ vanu_aggravated: Boolean,
+ targets: List[TargetValidation])
+
+object AggravatedDamage {
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param timing the timing for the damage application
+ * @param max_factor na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
+ def apply(info: AggravatedInfo,
+ effect_type: Aura,
+ timing: AggravatedTiming,
+ max_factor: Float,
+ targets: List[TargetValidation]): AggravatedDamage =
+ AggravatedDamage(
+ List(info),
+ effect_type,
+ timing,
+ max_factor,
+ cumulative_damage_degrade = true,
+ vanu_aggravated = false,
+ targets
+ )
+
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param timing the timing for the damage application
+ * @param max_factor na
+ * @param vanu_aggravated na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
+ def apply(info: AggravatedInfo,
+ effect_type: Aura,
+ timing: AggravatedTiming,
+ max_factor: Float,
+ vanu_aggravated: Boolean,
+ targets: List[TargetValidation]): AggravatedDamage =
+ AggravatedDamage(
+ List(info),
+ effect_type,
+ timing,
+ max_factor,
+ cumulative_damage_degrade = true,
+ vanu_aggravated,
+ targets
+ )
+
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param duration for how long the over-all effect is applied
+ * @param max_factor na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
+ def apply(info: AggravatedInfo,
+ effect_type: Aura,
+ duration: Long,
+ max_factor: Float,
+ targets: List[TargetValidation]): AggravatedDamage =
+ AggravatedDamage(
+ List(info),
+ effect_type,
+ AggravatedTiming(duration),
+ max_factor,
+ cumulative_damage_degrade = true,
+ vanu_aggravated = false,
+ targets
+ )
+
+ /**
+ * Overloaded constructor.
+ * @param info the specific kinds of aggravation damage available
+ * @param effect_type what effect is exhibited by this aggravated damage
+ * @param duration for how long the over-all effect is applied
+ * @param max_factor na
+ * @param vanu_aggravated na
+ * @param targets validation information indicating whether a certain entity is applicable for aggravation
+ */
+ def apply(info: AggravatedInfo,
+ effect_type: Aura,
+ duration: Long,
+ max_factor: Float,
+ vanu_aggravated: Boolean,
+ targets: List[TargetValidation]): AggravatedDamage =
+ AggravatedDamage(
+ List(info),
+ effect_type,
+ AggravatedTiming(duration),
+ max_factor,
+ cumulative_damage_degrade = true,
+ vanu_aggravated,
+ targets
+ )
+
+ def burning(resolution: ProjectileResolution.Value): ProjectileResolution.Value = {
+ resolution match {
+ case ProjectileResolution.AggravatedDirect => ProjectileResolution.AggravatedDirectBurn
+ case ProjectileResolution.AggravatedSplash => ProjectileResolution.AggravatedSplashBurn
+ case _ => resolution
+ }
+ }
+
+ def basicDamageType(resolution: ProjectileResolution.Value): DamageType.Value = {
+ resolution match {
+ case ProjectileResolution.AggravatedDirect | ProjectileResolution.AggravatedDirectBurn =>
+ DamageType.Direct
+ case ProjectileResolution.AggravatedSplash | ProjectileResolution.AggravatedSplashBurn =>
+ DamageType.Splash
+ case _ =>
+ DamageType.None
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
index 05705de3..a00b79e4 100644
--- a/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/Projectile.scala
@@ -1,6 +1,8 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
+import java.util.concurrent.atomic.AtomicLong
+
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
import net.psforever.objects.entity.SimpleWorldEntity
@@ -10,11 +12,11 @@ import net.psforever.types.Vector3
/**
* A summation of weapon (`Tool`) discharge.
- * @see `ProjectileDefinition`
- * `ToolDefinition`
- * `FireModeDefinition`
- * `SourceEntry`
- * `PlayerSource`
+ * @see `ProjectileDefinition`
+ * @see `ToolDefinition`
+ * @see `FireModeDefinition`
+ * @see `SourceEntry`
+ * @see `PlayerSource`
* @param profile an explanation of the damage that can be performed by this discharge
* @param tool_def the weapon that caused this discharge
* @param fire_mode the current fire mode of the tool used
@@ -25,6 +27,9 @@ import net.psforever.types.Vector3
* if not, then it is a type of vehicle (and owner should have a positive `seated` field)
* @param shot_origin where the projectile started
* @param shot_angle in which direction the projectile was aimed when it was discharged
+ * @param quality na
+ * @param id an exclusive identifier for this projectile;
+ * normally generated internally, but can be manually set
* @param fire_time when the weapon discharged was recorded;
* defaults to `System.nanoTime`
*/
@@ -36,6 +41,8 @@ final case class Projectile(
attribute_to: Int,
shot_origin: Vector3,
shot_angle: Vector3,
+ quality: ProjectileQuality = ProjectileQuality.Normal,
+ id: Long = Projectile.idGenerator.getAndIncrement(),
fire_time: Long = System.nanoTime
) extends PlanetSideGameObject {
Position = shot_origin
@@ -52,6 +59,32 @@ final case class Projectile(
val current: SimpleWorldEntity = new SimpleWorldEntity()
private var resolved: ProjectileResolution.Value = ProjectileResolution.Unresolved
+ /**
+ * Create a copy of this projectile with all the same information
+ * save for the quality.
+ * Used mainly for aggravated damage.
+ * It is important to note that the new projectile shares the (otherwise) exclusive id of the original.
+ * @param value the new quality
+ * @return a new `Projectile` entity
+ */
+ def quality(value: ProjectileQuality): Projectile = {
+ val projectile = Projectile(
+ profile,
+ tool_def,
+ fire_mode,
+ owner,
+ attribute_to,
+ shot_origin,
+ shot_angle,
+ value,
+ id,
+ fire_time
+ )
+ if(isMiss) projectile.Miss()
+ else if(isResolved) projectile.Resolve()
+ projectile
+ }
+
/**
* Mark the projectile as being "encountered" or "managed" at least once.
*/
@@ -80,6 +113,8 @@ object Projectile {
*/
final val rangeUID: Int = 40150
+ private val idGenerator: AtomicLong = new AtomicLong
+
/**
* Overloaded constructor for an `Unresolved` projectile.
* @param profile an explanation of the damage that can be performed by this discharge
diff --git a/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala b/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala
new file mode 100644
index 00000000..9a630845
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/ballistics/ProjectileQuality.scala
@@ -0,0 +1,36 @@
+//Copyright (c) 2020 PSForever
+package net.psforever.objects.ballistics
+
+/**
+ * Projectile quality is an external aspect of projectiles
+ * that is not dependent on hard-coded definitions of the entities
+ * used to compose the projectile such as the knowlegde of the emitting `Tool` (weapon).
+ * A flag or a damage modifier, depending on use.
+ * To the extent that it can be used as a numeric modifier,
+ * insists on defining a numeric modifier component rather to what it is trying to express.
+ * That numeric modifier does not have to be used for anything.
+ */
+sealed trait ProjectileQuality {
+ def mod: Float
+}
+
+/**
+ * Implement the numeric modifier with the value as one.
+ */
+sealed trait SameAsQuality extends ProjectileQuality {
+ def mod: Float = 1f
+}
+
+object ProjectileQuality {
+ /** Standard projectile quality. More of a flag than a modifier. */
+ case object Normal extends SameAsQuality
+
+ /** Quality that flags the first stage of aggravation (initial damage). */
+ case object AggravatesTarget extends SameAsQuality
+
+ /** The complete lack of quality. Even the numeric modifier is zeroed. */
+ case object Zeroed extends ProjectileQuality { def mod = 0f }
+
+ /** Assign a custom numeric qualifier value, usually to be applied to damage calculations. */
+ case class Modified(mod: Float) extends ProjectileQuality
+}
diff --git a/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala b/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala
index 87b56805..09b2e3b3 100644
--- a/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/ProjectileResolution.scala
@@ -2,16 +2,32 @@
package net.psforever.objects.ballistics
/**
- * An `Enumeration` of outcomes regarding what actually happened to the projectile.
+ * An `Enumeration` of outcomes regarding what actually happened to the projectile,
+ * complementing normal damage type distinction in directing damage calculations.
+ *
+ * Although the latter states reflect what sort of damage the projectile might perform - `Hit`, `Splash`, etc. -
+ * the state is more a communication about how that damage is interpreted by the server.
+ * For example, some projectiles:
+ * perform `Direct` damage, are reported by `HitMessage` packets, and resolve as `Hit`;
+ * or, perform `Direct` damage, are reported by `LashDamage` packets, and resolve as `Lash`.
+ * Furthermore, some projectiles:
+ * perform `Splash` damage, are reported by `SplashHitMessage` packets, and resolve as `Splash`;
+ * or, perform `Aggravated` damage, are reported by `SplashHitMessage` packets
+ * and resolve either as `AggravatedDirect` or as `AggravatedSplash`.
*/
object ProjectileResolution extends Enumeration {
type Type = Value
- val Unresolved, //original basic non-resolution
- MissedShot, //projectile did not encounter any collision object and was despawned
- Resolved, //a general "projectile encountered something" status with a more specific resolution
- Hit, //direct hit, one target
- Splash, //area of effect damage, potentially multiple targets
- Lash //lashing damage, potentially multiple targets
+ val
+ Unresolved, //original basic non-resolution
+ MissedShot, //projectile did not encounter any collision object and was despawned
+ Resolved, //a general "projectile encountered something" status with a more specific resolution
+ Hit, //direct hit, one target
+ Splash, //area of effect damage, potentially multiple targets
+ Lash, //lashing damage, potentially multiple targets
+ AggravatedDirect, //direct hit aggravated damage
+ AggravatedDirectBurn, //continuous direct hit aggravated damage
+ AggravatedSplash, //splashed aggravated damage
+ AggravatedSplashBurn //continuous splashed aggravated damage
= Value
}
diff --git a/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala b/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala
index a51fb03f..c06e3e16 100644
--- a/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala
+++ b/src/main/scala/net/psforever/objects/ballistics/ResolvedProjectile.scala
@@ -9,14 +9,13 @@ import net.psforever.types.Vector3
* about the interaction of weapons discharge and a target
* to the point that the original event might be reconstructed.
* Reenacting the calculations of this entry should always produce the same values.
- * @param resolution how the projectile hit was executed
* @param projectile the original projectile
* @param target what the projectile hit
* @param damage_model the kind of damage model to which the `target` is/was subject
* @param hit_pos where the projectile hit
*/
final case class ResolvedProjectile(
- resolution: ProjectileResolution.Value,
+ resolution : ProjectileResolution.Value,
projectile: Projectile,
target: SourceEntry,
damage_model: DamageResistanceModel,
diff --git a/src/main/scala/net/psforever/objects/ce/Deployable.scala b/src/main/scala/net/psforever/objects/ce/Deployable.scala
index 6448daf8..56736d35 100644
--- a/src/main/scala/net/psforever/objects/ce/Deployable.scala
+++ b/src/main/scala/net/psforever/objects/ce/Deployable.scala
@@ -33,7 +33,7 @@ object Deployable {
def Includes(category: DeployableCategory.Value): List[DeployedItem.Value] = {
(for {
- (ce, cat) <- deployablesToCategories
+ (ce: DeployedItem.Value, cat: DeployableCategory.Value) <- deployablesToCategories
if cat == category
} yield ce) toList
}
diff --git a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
index 69c6ed51..6c225fb0 100644
--- a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition
-import net.psforever.objects.ballistics.Projectiles
+import net.psforever.objects.ballistics.{AggravatedDamage, Projectiles}
import net.psforever.objects.equipment.JammingUnit
import net.psforever.objects.vital.damage.DamageModifiers
import net.psforever.objects.vital.{DamageType, StandardDamageProfile}
@@ -16,30 +16,68 @@ class ProjectileDefinition(objectId: Int)
with JammingUnit
with StandardDamageProfile
with DamageModifiers {
+ /** ascertain that this object is a valid projectile type */
private val projectileType: Projectiles.Value = Projectiles(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) */
private var accelerationUntil: Float = 0f
+ /** the type of damage that the projectile causes */
private var damageType: DamageType.Value = DamageType.None
+ /** an auxillary type of damage that the projectile causes */
private var damageTypeSecondary: DamageType.Value = DamageType.None
+ /** against Infantry targets, this projectile does not do armor damage */
+ private var damageToHealthOnly: Boolean = false
+ /** number of seconds before an airborne projectile's damage begins to degrade (s) */
private var degradeDelay: Float = 1f
+ /** the rate of degrade of projectile damage after the degrade delay */
private var degradeMultiplier: Float = 1f
+ /** the out-of-the-muzzle speed of a projectile (m/s) */
private var initialVelocity: Int = 1
+ /** for how long the projectile exists (s) */
private var lifespan: Float = 1f
+ /** for radial damage, how much damage has been lost the further away from the impact point (m) */
private var damageAtEdge: Float = 1f
+ /** for radial damage, the radial distance of the explosion effect (m) */
private var damageRadius: Float = 1f
+ /** for lashing damage, how far away a target will be affected by the projectile (m) */
private var lashRadius : Float = 0f
+ /** use a specific modifier as a part of damage calculations */
private var useDamage1Subtract: Boolean = false
- private var existsOnRemoteClients: Boolean = false //`true` spawns a server-managed object
- private var remoteClientData: (Int, Int) =
- (0, 0) //artificial values; for ObjectCreateMessage packet (oicw_little_buddy is undefined)
+ /** the projectile is represented by a server-side entity
+ * that is updated by the projectile owner
+ * and transmitted to all projectile observers;
+ * `true` spawns a server-managed object */
+ private var existsOnRemoteClients: Boolean = false
+ /** the values used by the `ObjectCreateMessage` packet for construction of the server-managed projectile
+ * `0, 0` are artificial values;
+ * the oicw_little_buddy is undefined for these values */
+ private var remoteClientData: (Int, Int) = (0, 0)
+ /** some other entity confers projectile damage;
+ * a set value should not `None` and not `0` but is preferred to be the damager's uid */
private var damageProxy: Option[Int] = None
+ /** this projectile follows its target, after a fashion */
private var autoLock: Boolean = false
+ /** na;
+ * currently used with jammer properties only */
private var additionalEffect: Boolean = false
+ /** the projectile tries to confer the jammered status effect to its target(s) */
private var jammerProjectile: Boolean = false
+ /** projectile takes the form of a type of "grenade";
+ * grenades arc with gravity rather than travel in a relatively straight path */
+ private var grenade_projectile : Boolean = false
+ /** projectile tries to confers aggravated damage burn to its target */
+ private var aggravated_damage : Option[AggravatedDamage] = None
//derived calculations
+ /** the calculated distance at which the projectile have traveled far enough to despawn (m);
+ * typically handled as the projectile no longer performing damage;
+ * occasionally, this value is purely mathematical as opposed to realistic, e.g., the melee weapons */
private var distanceMax: Float = 0f
+ /** how far the projectile will travel while accelerating (m) */
private var distanceFromAcceleration: Float = 0f
+ /** how far the projectile will travel while no degrading (m) */
private var distanceNoDegrade: Float = 0f
+ /** after acceleration, if any, what is the final speed of the projectile (m/s) */
private var finalVelocity: Float = 0f
Name = "projectile"
Modifiers = DamageModifiers.DistanceDegrade
@@ -81,6 +119,17 @@ class ProjectileDefinition(objectId: Int)
ProjectileDamageTypeSecondary
}
+ def ProjectileDamageTypes : Set[DamageType.Value] = {
+ Set(damageType, damageTypeSecondary).filterNot(_ == DamageType.None)
+ }
+
+ def DamageToHealthOnly : Boolean = damageToHealthOnly
+
+ def DamageToHealthOnly_=(healthOnly: Boolean) : Boolean = {
+ damageToHealthOnly = healthOnly
+ DamageToHealthOnly
+ }
+
def DegradeDelay: Float = degradeDelay
def DegradeDelay_=(degradeDelay: Float): Float = {
@@ -174,7 +223,23 @@ class ProjectileDefinition(objectId: Int)
JammerProjectile
}
- def DistanceMax: Float = distanceMax //accessor only
+ def GrenadeProjectile : Boolean = grenade_projectile
+
+ def GrenadeProjectile_=(isGrenade : Boolean) : Boolean = {
+ grenade_projectile = isGrenade
+ GrenadeProjectile
+ }
+
+ def Aggravated : Option[AggravatedDamage] = aggravated_damage
+
+ def Aggravated_=(damage : AggravatedDamage) : Option[AggravatedDamage] = Aggravated_=(Some(damage))
+
+ def Aggravated_=(damage : Option[AggravatedDamage]) : Option[AggravatedDamage] = {
+ aggravated_damage = damage
+ Aggravated
+ }
+
+ def DistanceMax : Float = distanceMax //accessor only
def DistanceFromAcceleration: Float = distanceFromAcceleration //accessor only
diff --git a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala
index d69ee638..7bfc8f28 100644
--- a/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala
+++ b/src/main/scala/net/psforever/objects/equipment/EffectTarget.scala
@@ -89,10 +89,10 @@ object EffectTarget {
false
}
- def AMS(target: PlanetSideGameObject): Boolean =
+ def Vehicle(target: PlanetSideGameObject): Boolean =
target match {
case v: Vehicle =>
- v.Health > 0 && v.Definition == GlobalDefinitions.ams
+ v.Health > 0
case _ =>
false
}
@@ -104,5 +104,21 @@ object EffectTarget {
case _ =>
false
}
+
+ def AMS(target: PlanetSideGameObject): Boolean =
+ target match {
+ case v: Vehicle =>
+ v.Health > 0 && v.Definition == GlobalDefinitions.ams
+ case _ =>
+ false
+ }
+
+ def Aircraft(target: PlanetSideGameObject): Boolean =
+ target match {
+ case v: Vehicle =>
+ GlobalDefinitions.isFlightVehicle(v.Definition) && v.Health > 0
+ case _ =>
+ false
+ }
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/aura/Aura.scala b/src/main/scala/net/psforever/objects/serverobject/aura/Aura.scala
new file mode 100644
index 00000000..f78867e0
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/serverobject/aura/Aura.scala
@@ -0,0 +1,33 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.serverobject.aura
+
+/**
+ * An effect that can be emitted by a target game object entity.
+ */
+sealed class Aura()
+
+/**
+ * Visual effects emitted by a target, usually a `Player` entity.
+ * Most often paired with aggravated damage.
+ * Unrelated to the effects emitted by obtaining and transporting
+ * the lattice logic unit, or a facility module, or the rabbit ball.
+ */
+object Aura {
+ /** Since `None` is an actual effect, the "no effect" default is repurposed as "Nothing". */
+ final case object Nothing extends Aura
+
+ /** Conferred by the `aphelion_starfire_projectile`. */
+ final case object None extends Aura
+
+ /** A green emission. */
+ final case object Plasma extends Aura
+
+ /** A purple emission. */
+ final case object Comet extends Aura
+
+ /** A white and yellow starburst emission. */
+ final case object Napalm extends Aura
+
+ /** A red and orange emission. */
+ final case object Fire extends Aura
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/aura/AuraContainer.scala b/src/main/scala/net/psforever/objects/serverobject/aura/AuraContainer.scala
new file mode 100644
index 00000000..78d42069
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/serverobject/aura/AuraContainer.scala
@@ -0,0 +1,25 @@
+package net.psforever.objects.serverobject.aura
+
+import net.psforever.objects.serverobject.aura.{Aura => AuraEffect}
+
+/**
+ * An entity that can display specific special effects that decorate its model.
+ * These animations confer information about the nature of some status that is affecting the target entity.
+ */
+trait AuraContainer {
+ private var aura : Set[AuraEffect] = Set.empty[AuraEffect]
+
+ def Aura : Set[AuraEffect] = aura
+
+ def AddEffectToAura(effect : AuraEffect) : Set[AuraEffect] = {
+ if(effect != AuraEffect.None) {
+ aura = aura + effect
+ }
+ Aura
+ }
+
+ def RemoveEffectFromAura(effect : AuraEffect) : Set[AuraEffect] = {
+ aura = aura - effect
+ Aura
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/aura/AuraEffectBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/aura/AuraEffectBehavior.scala
new file mode 100644
index 00000000..1a041b62
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/serverobject/aura/AuraEffectBehavior.scala
@@ -0,0 +1,186 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.serverobject.aura
+
+import akka.actor.{Actor, Cancellable}
+import net.psforever.objects.Default
+import net.psforever.objects.serverobject.PlanetSideServerObject
+
+import scala.collection.mutable
+import scala.concurrent.ExecutionContext.Implicits.global
+import scala.concurrent.duration._
+
+/**
+ * A mixin that governs the addition, display, and removal of aura particle effects
+ * on a target with control agency.
+ * @see `Aura`
+ * @see `AuraContainer`
+ * @see `PlayerControl`
+ */
+trait AuraEffectBehavior {
+ _ : Actor =>
+ /** active aura effects are monotonic, but the timer will be updated for continuing and cancelling effects as well
+ * only effects that are initialized to this mapping are approved for display on this target
+ * key - aura effect; value - the timer for that effect
+ * @see `ApplicableEffect`
+ */
+ private val effectToTimer: mutable.HashMap[Aura, AuraEffectBehavior.Entry] = mutable.HashMap.empty[Aura, AuraEffectBehavior.Entry]
+
+ def AuraTargetObject: AuraEffectBehavior.Target
+
+ val auraBehavior: Receive = {
+ case AuraEffectBehavior.StartEffect(effect, duration) =>
+ StartAuraEffect(effect, duration)
+
+ case AuraEffectBehavior.EndEffect(effect) =>
+ EndAuraEffectAndUpdate(effect)
+
+ case AuraEffectBehavior.EndAllEffects() =>
+ EndAllEffectsAndUpdate()
+ }
+
+ /**
+ * Only pre-apporved aura effects will be emitted by this target.
+ * @param effect the aura effect
+ */
+ def ApplicableEffect(effect: Aura): Unit = {
+ //create entry
+ effectToTimer += effect -> AuraEffectBehavior.Entry()
+ }
+
+ /**
+ * An aura particle effect is to be emitted by the target.
+ * If the effect was not previously applied to the target in an ongoing manner,
+ * animate it appropriately.
+ * @param effect the effect to be emitted
+ * @param duration for how long the effect will be emitted
+ * @return the active effect index number
+ */
+ def StartAuraEffect(effect: Aura, duration: Long): Unit = {
+ val obj = AuraTargetObject
+ val auraEffectsBefore = obj.Aura.size
+ if(StartAuraTimer(effect, duration) && obj.AddEffectToAura(effect).size > auraEffectsBefore) {
+ //new effect; update visuals
+ UpdateAuraEffect(AuraTargetObject)
+ }
+ }
+
+ /**
+ * As long as the effect has been approved for this target,
+ * the timer will either start if it is stopped or has never been started,
+ * or the timer will stop and be recreated with the new duration if is currently running for a shoreter amount of time.
+ * @param effect the effect to be emitted
+ * @param duration for how long the effect will be emitted
+ * @return `true`, if the timer was started or restarted;
+ * `false`, otherwise
+ */
+ private def StartAuraTimer(effect: Aura, duration: Long): Boolean = {
+ //pair aura effect with entry
+ (effectToTimer.get(effect) match {
+ case Some(timer) if timer.start + timer.duration < System.currentTimeMillis() + duration =>
+ timer.cancel()
+ Some(effect)
+ case _ =>
+ None
+ }) match {
+ case None =>
+ false
+ case Some(_) =>
+ //retime
+ effectToTimer(effect) = AuraEffectBehavior.Entry(
+ duration,
+ context.system.scheduler.scheduleOnce(duration milliseconds, self, AuraEffectBehavior.EndEffect(effect))
+ )
+ true
+ }
+ }
+
+ /**
+ * Stop the target entity from emitting the aura particle effect, if it currently is.
+ * @param effect the target effect
+ * @return `true`, if the effect was being emitted but has been stopped
+ * `false`, if the effect was not approved or is not being emitted
+ */
+ def EndAuraEffect(effect: Aura): Boolean = {
+ effectToTimer.get(effect) match {
+ case Some(timer) if !timer.isCancelled =>
+ timer.cancel()
+ //effectToTimer(effect) = Default.Cancellable
+ AuraTargetObject.RemoveEffectFromAura(effect)
+ true
+ case _ =>
+ false
+ }
+ }
+
+ /**
+ * Stop the target entity from emitting all aura particle effects.
+ */
+ def EndAllEffects() : Unit = {
+ effectToTimer.keysIterator.foreach { effect =>
+ effectToTimer(effect).cancel()
+ //effectToTimer(effect) = Default.Cancellable
+ }
+ val obj = AuraTargetObject
+ obj.Aura.foreach { obj.RemoveEffectFromAura }
+ }
+
+ /**
+ * Stop the target entity from emitting the aura particle effect, if it currently is.
+ * If the effect has been stopped, animate the new particle effect state.
+ */
+ def EndAuraEffectAndUpdate(effect: Aura) : Unit = {
+ if(EndAuraEffect(effect)) {
+ UpdateAuraEffect(AuraTargetObject)
+ }
+ }
+
+ /**
+ * Stop the target entity from emitting all aura particle effects.
+ * Animate the new particle effect state.
+ */
+ def EndAllEffectsAndUpdate() : Unit = {
+ EndAllEffects()
+ UpdateAuraEffect(AuraTargetObject)
+ }
+
+ /**
+ * Is the target entity emitting the aura effect?
+ * @param effect the effect being tested
+ * @return `true`, if the effect is currently being emitted;
+ * `false`, otherwise
+ */
+ def TestForEffect(effect: Aura): Boolean = {
+ effectToTimer.get(effect) match {
+ case None => false
+ case Some(timer) => timer.isCancelled
+ }
+ }
+
+ /**
+ * An override callback to display aura effects emitted.
+ * @param target the entity from which the aura effects are being emitted
+ */
+ def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit
+}
+
+object AuraEffectBehavior {
+ type Target = PlanetSideServerObject with AuraContainer
+
+ case class Entry(duration: Long, timer: Cancellable) extends Cancellable {
+ val start: Long = System.currentTimeMillis()
+
+ override def isCancelled : Boolean = timer.isCancelled
+
+ override def cancel(): Boolean = timer.cancel()
+ }
+
+ object Entry {
+ def apply(): Entry = Entry(0, Default.Cancellable)
+ }
+
+ final case class StartEffect(effect: Aura, duration: Long)
+
+ final case class EndEffect(aura: Aura)
+
+ final case class EndAllEffects()
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
new file mode 100644
index 00000000..ad211cdb
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/AggravatedBehavior.scala
@@ -0,0 +1,209 @@
+// Copyright (c) 2020 PSForever
+package net.psforever.objects.serverobject.damage
+
+import akka.actor.{Actor, Cancellable}
+import net.psforever.objects.ballistics._
+import net.psforever.objects.serverobject.aura.Aura
+import net.psforever.objects.vital.{DamageType, Vitality}
+
+import scala.collection.mutable
+import scala.concurrent.ExecutionContext.Implicits.global
+import scala.concurrent.duration._
+
+trait AggravatedBehavior {
+ _ : Actor with Damageable =>
+ private val entryIdToEntry: mutable.LongMap[AggravatedBehavior.Entry] =
+ mutable.LongMap.empty[AggravatedBehavior.Entry]
+ private val aggravationToTimer: mutable.LongMap[Cancellable] =
+ mutable.LongMap.empty[Cancellable]
+ /** ongoing flag to indicate whether the target is being afflicted by any form of aggravated damage */
+ private var ongoingAggravated: Boolean = false
+
+ def AggravatedObject: AggravatedBehavior.Target
+
+ def TryAggravationEffectActivate(data: ResolvedProjectile): Option[AggravatedDamage] = {
+ val projectile = data.projectile
+ projectile.profile.Aggravated match {
+ case Some(damage)
+ if projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated) &&
+ damage.info.exists(_.damage_type == AggravatedDamage.basicDamageType(data.resolution)) &&
+ damage.effect_type != Aura.Nothing &&
+ (projectile.quality == ProjectileQuality.AggravatesTarget ||
+ damage.targets.exists(validation => validation.test(AggravatedObject))) =>
+ TryAggravationEffectActivate(damage, data)
+ case _ =>
+ None
+ }
+ }
+
+ private def TryAggravationEffectActivate(
+ aggravation: AggravatedDamage,
+ data: ResolvedProjectile
+ ): Option[AggravatedDamage] = {
+ val effect = aggravation.effect_type
+ if(CheckForUniqueUnqueuedProjectile(data.projectile)) {
+ val sameEffect = entryIdToEntry.values.filter(entry => entry.effect == effect)
+ if(sameEffect.isEmpty || sameEffect.nonEmpty && aggravation.cumulative_damage_degrade) {
+ SetupAggravationEntry(aggravation, data)
+ Some(aggravation)
+ }
+ else {
+ None
+ }
+ }
+ else {
+ None
+ }
+ }
+
+ private def CheckForUniqueUnqueuedProjectile(projectile : Projectile): Boolean = {
+ !entryIdToEntry.values.exists { entry => entry.data.projectile.id == projectile.id }
+ }
+
+ private def SetupAggravationEntry(aggravation: AggravatedDamage, data: ResolvedProjectile): Boolean = {
+ val effect = aggravation.effect_type
+ aggravation.info.find(_.damage_type == AggravatedDamage.basicDamageType(data.resolution)) match {
+ case Some(info) =>
+ val timing = aggravation.timing
+ val duration = timing.duration
+ //setup effect
+ val id = data.projectile.id
+ //setup timer data
+ val (tick: Long, iterations: Int) = timing.ticks match {
+ case Some(n) if n < 1 =>
+ val rate = info.infliction_rate
+ (rate, (duration / rate).toInt)
+ case Some(ticks) =>
+ (duration / ticks, ticks)
+ case None =>
+ (1000L, (duration / 1000).toInt)
+ }
+ //quality per tick
+ val totalPower = (duration.toFloat / info.infliction_rate).toInt - 1
+ val averagePowerPerTick = totalPower.toFloat / iterations
+ val lastTickRemainder = totalPower - averagePowerPerTick * iterations
+ val qualityPerTick: List[Float] = if (lastTickRemainder > 0) {
+ 0f +: List.fill[Float](iterations - 1)(averagePowerPerTick) :+ (lastTickRemainder + averagePowerPerTick)
+ }
+ else {
+ 0f +: List.fill[Float](iterations)(averagePowerPerTick)
+ }
+ //pair id with entry
+ PairIdWithAggravationEntry(id, effect, tick, data, data.target, qualityPerTick)
+ //pair id with timer
+ aggravationToTimer += id -> context.system.scheduler.scheduleOnce(tick milliseconds, self, AggravatedBehavior.Aggravate(id, iterations))
+ ongoingAggravated = true
+ true
+ case _ =>
+ false
+ }
+ }
+
+ private def PairIdWithAggravationEntry(
+ id: Long,
+ effect: Aura,
+ retime: Long,
+ data: ResolvedProjectile,
+ target: SourceEntry,
+ powerOffset: List[Float]
+ ): AggravatedBehavior.Entry = {
+ val aggravatedDamageInfo = ResolvedProjectile(
+ AggravatedDamage.burning(data.resolution),
+ data.projectile,
+ target,
+ data.damage_model,
+ data.hit_pos
+ )
+ val entry = AggravatedBehavior.Entry(id, effect, retime, aggravatedDamageInfo, powerOffset)
+ entryIdToEntry += id -> entry
+ entry
+ }
+
+ val aggravatedBehavior: Receive = {
+ case AggravatedBehavior.Aggravate(id, 0) =>
+ AggravationCleanup(id)
+
+ case AggravatedBehavior.Aggravate(id, iteration) =>
+ RetimeEventAndPerformAggravation(id, iteration, None)
+ }
+
+ private def RetimeEventAndPerformAggravation(id: Long, iteration: Int, time: Option[Long]) : Unit = {
+ RetimeAggravation(id, iteration - 1, time) match {
+ case Some(entry) =>
+ PerformAggravation(entry, iteration)
+ case _ => ;
+ }
+ }
+
+ private def RetimeAggravation(
+ id: Long,
+ iteration: Int,
+ time: Option[Long]
+ ): Option[AggravatedBehavior.Entry] = {
+ CleanupAggravationTimer(id)
+ entryIdToEntry.get(id) match {
+ case out @ Some(oldEntry) =>
+ aggravationToTimer += id -> context.system.scheduler.scheduleOnce(
+ time.getOrElse(oldEntry.retime) milliseconds,
+ self,
+ AggravatedBehavior.Aggravate(id, iteration)
+ )
+ out
+ case _ =>
+ AggravationCleanup(id)
+ None
+ }
+ }
+
+ def RemoveAggravatedEntry(id: Long): Aura = {
+ entryIdToEntry.remove(id) match {
+ case Some(entry) =>
+ ongoingAggravated = entryIdToEntry.nonEmpty
+ entry.data.projectile.profile.Aggravated.get.effect_type
+ case _ =>
+ Aura.Nothing
+ }
+ }
+
+ def CleanupAggravationTimer(id: Long): Unit = {
+ //remove and cancel timer
+ aggravationToTimer.remove(id) match {
+ case Some(timer) => timer.cancel()
+ case _ => ;
+ }
+ }
+
+ def AggravationCleanup(id: Long): Unit = {
+ RemoveAggravatedEntry(id)
+ CleanupAggravationTimer(id)
+ }
+
+ def EndAllAggravation(): Unit = {
+ entryIdToEntry.clear()
+ aggravationToTimer.values.foreach { _.cancel() }
+ aggravationToTimer.clear()
+ }
+
+ def AggravatedReaction: Boolean = ongoingAggravated
+
+ private def PerformAggravation(entry: AggravatedBehavior.Entry, tick: Int = 0): Unit = {
+ val data = entry.data
+ val model = data.damage_model
+ val aggravatedProjectileData = ResolvedProjectile(
+ data.resolution,
+ data.projectile.quality(ProjectileQuality.Modified(entry.qualityPerTick(tick))),
+ data.target,
+ model,
+ data.hit_pos
+ )
+ takesDamage.apply(Vitality.Damage(model.Calculate(aggravatedProjectileData)))
+ }
+}
+
+object AggravatedBehavior {
+ type Target = Damageable.Target
+
+ private case class Entry(id: Long, effect: Aura, retime: Long, data: ResolvedProjectile, qualityPerTick: List[Float])
+
+ private case class Aggravate(id: Long, iterations: Int)
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
index 5dc87756..e36b3c95 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
@@ -8,6 +8,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.resolution.ResolutionCalculations
/**
* The base "control" `Actor` mixin for damage-handling code.
@@ -24,16 +25,35 @@ trait Damageable {
*/
def DamageableObject: Damageable.Target
- /** the official mixin hook; `orElse` onto the "control" `Actor` `receive` */
- final val takesDamage: Receive = TakesDamage
+ /** the official mixin hook;
+ * `orElse` onto the "control" `Actor` `receive`; or,
+ * cite the `originalTakesDamage` protocol during inheritance overrides */
+ val takesDamage: Receive = {
+ case Vitality.Damage(damage_func) =>
+ val obj = DamageableObject
+ if (obj.CanDamage) {
+ PerformDamage(obj, damage_func)
+ }
+ }
+
+ /** a duplicate of the core implementation for the default mixin hook, for use in overriding */
+ final val originalTakesDamage: Receive = {
+ case Vitality.Damage(damage_func) =>
+ val obj = DamageableObject
+ if (obj.CanDamage) {
+ PerformDamage(obj, damage_func)
+ }
+ }
/**
- * Implementation of the mixin hook will be provided by a child class.
- * Override this method only when directly implementing.
- * @see `takesDamage`
- * @see `DamageableAmenity.PerformDamage`
+ * Assess the vital statistics of the target, apply the damage, and determine if any of those statistics changed.
+ * By default, only take an interest in the change of "health".
+ * If implementing custom damage with no new message handling, override this method.
+ * @see `ResolutionCalculations.Output`
+ * @param target the entity to be damaged
+ * @param applyDamageTo the function that applies the damage to the target in a target-tailored fashion
*/
- protected def TakesDamage: Receive
+ protected def PerformDamage(target: Damageable.Target, applyDamageTo: ResolutionCalculations.Output): Unit
}
object Damageable {
@@ -59,7 +79,7 @@ object Damageable {
*/
def CanDamage(obj: Vitality with FactionAffinity, damage: Int, data: ResolvedProjectile): Boolean = {
val definition = obj.Definition
- damage > 0 &&
+ (damage > 0 || data.projectile.profile.Aggravated.nonEmpty) &&
definition.Damageable &&
(definition.DamageableByFriendlyFire ||
(data.projectile.owner.Faction != obj.Faction ||
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
index e4591747..a2db8e3c 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala
@@ -1,10 +1,8 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
-import akka.actor.Actor.Receive
import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.equipment.JammableUnit
-import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideGUID
@@ -42,23 +40,6 @@ trait DamageableEntity extends Damageable {
DamageLog(s"${name.substring(slashPoint + 1, name.length - 1)}: $msg")
}
- /**
- * Catch the expected damage message and apply checks to the target.
- * If adding custom message handling in an future child implementation,
- * override this method and call `super.TakesDamage.orElse { ... }`.
- * @see `Damageable.TakesDamage`
- * @see `ResolutionCalcultions.Output`
- * @see `Vitality.CanDamage`
- * @see `Vitality.Damage`
- */
- protected def TakesDamage: Receive = {
- case Vitality.Damage(damage_func) =>
- val obj = DamageableObject
- if (obj.CanDamage) {
- PerformDamage(obj, damage_func)
- }
- }
-
/**
* Assess the vital statistics of the target, apply the damage, and determine if any of those statistics changed.
* By default, only take an interest in the change of "health".
@@ -108,7 +89,7 @@ trait DamageableEntity extends Damageable {
* @param cause historical information about the damage
* @param damage the amount of damage
*/
- protected def HandleDamage(target: Damageable.Target, cause: ResolvedProjectile, damage: Int): Unit = {
+ protected def HandleDamage(target: Damageable.Target, cause: ResolvedProjectile, damage: Any): Unit = {
if (!target.Destroyed && target.Health <= target.Definition.DamageDestroysAt) {
DestructionAwareness(target, cause)
} else {
@@ -122,8 +103,12 @@ trait DamageableEntity extends Damageable {
* @param cause historical information about the damage
* @param amount the amount of damage
*/
- protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
- DamageableEntity.DamageAwareness(target, cause, amount)
+ protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ amount match {
+ case value: Int =>
+ DamageableEntity.DamageAwareness(target, cause, value)
+ case _ => ;
+ }
}
/**
@@ -162,16 +147,22 @@ object DamageableEntity {
if (Damageable.CanJammer(target, cause)) {
target.Actor ! JammableUnit.Jammered(cause)
}
- if (amount > 0) {
+ if (DamageToHealth(target, cause, amount)) {
+ target.Zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ }
+ }
+
+ def DamageToHealth(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Boolean = {
+ if (amount > 0 && !target.Destroyed) {
val zone = target.Zone
- if (!target.Destroyed) {
- val tguid = target.GUID
- zone.AvatarEvents ! AvatarServiceMessage(
- zone.id,
- AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health)
- )
- }
- zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ zone.AvatarEvents ! AvatarServiceMessage(
+ zone.id,
+ AvatarAction.PlanetsideAttributeToAll(target.GUID, 0, target.Health)
+ )
+ true
+ }
+ else {
+ false
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
index cc3e5a8b..abfb7922 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
@@ -26,8 +26,13 @@ object DamageableMountable {
* @see `Zone.LivePlayers`
* @param target the entity being damaged
* @param cause historical information about the damage
+ * @param countableDamage the amount of damage being done, translating to the intensity of the damage indicator
*/
- def DamageAwareness(target: Damageable.Target with Mountable, cause: ResolvedProjectile): Unit = {
+ def DamageAwareness(
+ target: Damageable.Target with Mountable,
+ cause: ResolvedProjectile,
+ countableDamage: Int
+ ): Unit = {
val zone = target.Zone
val events = zone.AvatarEvents
val occupants = target.Seats.values.collect {
@@ -38,9 +43,10 @@ object DamageableMountable {
case pSource: PlayerSource => //player damage
val name = pSource.Name
(zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match {
- case Some(player) => AvatarAction.HitHint(player.GUID, player.GUID)
+ case Some(player) =>
+ AvatarAction.HitHint(player.GUID, player.GUID)
case None =>
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, pSource.Position))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position))
}) match {
case AvatarAction.HitHint(_, guid) =>
occupants.map { tplayer => (tplayer.Name, AvatarAction.HitHint(guid, tplayer.GUID)) }
@@ -48,7 +54,7 @@ object DamageableMountable {
occupants.map { tplayer => (tplayer.Name, msg) }
}
case source => //object damage
- val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, source.Position))
+ val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position))
occupants.map { tplayer => (tplayer.Name, msg) }
}).foreach {
case (channel, msg) =>
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
index c3d26a3d..ceea8af0 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala
@@ -1,43 +1,55 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
-import akka.actor.Actor.Receive
+import akka.actor.Actor
import net.psforever.objects.{Vehicle, Vehicles}
import net.psforever.objects.ballistics.ResolvedProjectile
+import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.damage.Damageable.Target
+import net.psforever.objects.vital.DamageType
import net.psforever.objects.vital.resolution.ResolutionCalculations
import net.psforever.services.Service
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.DamageWithPositionMessage
+import net.psforever.types.Vector3
import scala.concurrent.duration._
/**
* The "control" `Actor` mixin for damage-handling code for `Vehicle` objects.
*/
-trait DamageableVehicle extends DamageableEntity {
+trait DamageableVehicle
+ extends DamageableEntity
+ with AggravatedBehavior {
+ _ : Actor =>
- /** vehicles (may) have shields; they need to be handled */
- private var handleDamageToShields: Boolean = false
+ def damageableVehiclePostStop(): Unit = {
+ EndAllAggravation()
+ }
/** whether or not the vehicle has been damaged directly, report that damage has occurred */
private var reportDamageToVehicle: Boolean = false
def DamageableObject: Vehicle
+ def AggravatedObject : Vehicle = DamageableObject
- override protected def TakesDamage: Receive =
- super.TakesDamage.orElse {
- case DamageableVehicle.Damage(cause, damage) =>
- //cargo vehicles inherit feedback from carrier
- reportDamageToVehicle = damage > 0
- DamageAwareness(DamageableObject, cause, amount = 0)
+ override val takesDamage: Receive =
+ originalTakesDamage
+ .orElse(aggravatedBehavior)
+ .orElse {
+ case DamageableVehicle.Damage(cause, damage) =>
+ //cargo vehicles inherit feedback from carrier
+ reportDamageToVehicle = damage > 0
+ DamageAwareness(DamageableObject, cause, amount = 0)
- case DamageableVehicle.Destruction(cause) =>
- //cargo vehicles are destroyed when carrier is destroyed
- val obj = DamageableObject
- obj.Health = 0
- obj.History(cause)
- DestructionAwareness(obj, cause)
- }
+ case DamageableVehicle.Destruction(cause) =>
+ //cargo vehicles are destroyed when carrier is destroyed
+ val obj = DamageableObject
+ obj.Health = 0
+ obj.History(cause)
+ DestructionAwareness(obj, cause)
+ }
/**
* Vehicles may have charged shields that absorb damage before the vehicle's own health is affected.
@@ -62,53 +74,13 @@ trait DamageableVehicle extends DamageableEntity {
target,
s"BEFORE=$originalHealth/$originalShields, AFTER=$health/$shields, CHANGE=$damageToHealth/$damageToShields"
)
- handleDamageToShields = damageToShields > 0
- HandleDamage(target, cause, damageToHealth + damageToShields)
+ HandleDamage(target, cause, (damageToHealth, damageToShields))
} else {
obj.Health = originalHealth
obj.Shields = originalShields
}
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
- val obj = DamageableObject
- val handleShields = handleDamageToShields
- handleDamageToShields = false
- val handleReport = reportDamageToVehicle || amount > 0
- reportDamageToVehicle = false
- if (Damageable.CanDamageOrJammer(target, amount, cause)) {
- super.DamageAwareness(target, cause, amount)
- }
- if (handleReport) {
- DamageableMountable.DamageAwareness(obj, cause)
- }
- DamageableVehicle.DamageAwareness(obj, cause, amount, handleShields)
- }
-
- override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
- super.DestructionAwareness(target, cause)
- val obj = DamageableObject
- DamageableMountable.DestructionAwareness(obj, cause)
- DamageableVehicle.DestructionAwareness(obj, cause)
- DamageableWeaponTurret.DestructionAwareness(obj, cause)
- }
-}
-
-object DamageableVehicle {
-
- /**
- * Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
- * @param cause historical information about damage
- */
- private case class Damage(cause: ResolvedProjectile, amount: Int)
-
- /**
- * Message for instructing the target's cargo vehicles that their carrier is destroyed,
- * and they should be destroyed too.
- * @param cause historical information about damage
- */
- private case class Destruction(cause: ResolvedProjectile)
-
/**
* Most all vehicles and the weapons mounted to them can jam
* if the projectile that strikes (near) them has jammering properties.
@@ -121,25 +93,76 @@ object DamageableVehicle {
* @see `VehicleServiceMessage`
* @param target the entity being destroyed
* @param cause historical information about the damage
- * @param damage how much damage was performed
- * @param damageToShields dispatch a shield strength update
+ * @param amount how much damage was performed
*/
- def DamageAwareness(target: Vehicle, cause: ResolvedProjectile, damage: Int, damageToShields: Boolean): Unit = {
- //alert cargo occupants to damage source
- target.CargoHolds.values.foreach(hold => {
- hold.Occupant match {
- case Some(cargo) =>
- cargo.Actor ! DamageableVehicle.Damage(cause, damage + (if (damageToShields) 1 else 0))
- case None => ;
+ override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ val obj = DamageableObject
+ val zone = target.Zone
+ val events = zone.VehicleEvents
+ val targetGUID = target.GUID
+ val zoneId = zone.id
+ val vehicleChannel = s"${obj.Actor}"
+ val (damageToHealth, damageToShields, totalDamage) = amount match {
+ case (a: Int, b: Int) => (a, b, a+b)
+ case _ => (0, 0, 0)
+ }
+ var announceConfrontation: Boolean = reportDamageToVehicle || totalDamage > 0
+ val aggravated = TryAggravationEffectActivate(cause) match {
+ case Some(_) =>
+ announceConfrontation = true
+ false
+ case _ =>
+ cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ }
+ reportDamageToVehicle = false
+
+ //log historical event
+ target.History(cause)
+ //damage
+ if (Damageable.CanDamageOrJammer(target, totalDamage, cause)) {
+ //jammering
+ if (Damageable.CanJammer(target, cause)) {
+ target.Actor ! JammableUnit.Jammered(cause)
}
- })
- //shields
- if (damageToShields) {
- val zone = target.Zone
- zone.VehicleEvents ! VehicleServiceMessage(
- s"${target.Actor}",
- VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields)
- )
+ //stat changes
+ if (damageToShields > 0) {
+ events ! VehicleServiceMessage(
+ vehicleChannel,
+ VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 68, obj.Shields)
+ )
+ announceConfrontation = true
+ }
+ if (damageToHealth > 0) {
+ events ! VehicleServiceMessage(
+ zoneId,
+ VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health)
+ )
+ announceConfrontation = true
+ }
+ }
+ if (announceConfrontation) {
+ if (aggravated) {
+ val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(totalDamage, Vector3.Zero))
+ obj.Seats.values
+ .collect { case seat if seat.Occupant.nonEmpty => seat.Occupant.get.Name }
+ .foreach { channel =>
+ events ! VehicleServiceMessage(channel, msg)
+ }
+ }
+ else {
+ //activity on map
+ zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ //alert to damage source
+ DamageableMountable.DamageAwareness(obj, cause, totalDamage)
+ }
+ //alert cargo occupants to damage source
+ obj.CargoHolds.values.foreach(hold => {
+ hold.Occupant match {
+ case Some(cargo) =>
+ cargo.Actor ! DamageableVehicle.Damage(cause, totalDamage)
+ case None => ;
+ }
+ })
}
}
@@ -164,10 +187,15 @@ object DamageableVehicle {
* @param target the entity being destroyed
* @param cause historical information about the damage
*/
- def DestructionAwareness(target: Vehicle, cause: ResolvedProjectile): Unit = {
+ override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
+ super.DestructionAwareness(target, cause)
+ val obj = DamageableObject
+ DamageableMountable.DestructionAwareness(obj, cause)
val zone = target.Zone
+ //aggravation cancel
+ EndAllAggravation()
//cargo vehicles die with us
- target.CargoHolds.values.foreach(hold => {
+ obj.CargoHolds.values.foreach(hold => {
hold.Occupant match {
case Some(cargo) =>
cargo.Actor ! DamageableVehicle.Destruction(cause)
@@ -175,10 +203,10 @@ object DamageableVehicle {
}
})
//special considerations for certain vehicles
- Vehicles.BeforeUnloadVehicle(target, zone)
+ Vehicles.BeforeUnloadVehicle(obj, zone)
//shields
- if (target.Shields > 0) {
- target.Shields = 0
+ if (obj.Shields > 0) {
+ obj.Shields = 0
zone.VehicleEvents ! VehicleServiceMessage(
zone.id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0)
@@ -186,5 +214,22 @@ object DamageableVehicle {
}
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
target.ClearHistory()
+ DamageableWeaponTurret.DestructionAwareness(obj, cause)
}
}
+
+object DamageableVehicle {
+
+ /**
+ * Message for instructing the target's cargo vehicles about a damage source affecting their carrier.
+ * @param cause historical information about damage
+ */
+ private case class Damage(cause: ResolvedProjectile, amount: Int)
+
+ /**
+ * Message for instructing the target's cargo vehicles that their carrier is destroyed,
+ * and they should be destroyed too.
+ * @param cause historical information about damage
+ */
+ private case class Destruction(cause: ResolvedProjectile)
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
index b7fab97a..f4ff4b2c 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala
@@ -1,30 +1,97 @@
//Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.damage
+import akka.actor.Actor
import net.psforever.objects.ballistics.ResolvedProjectile
+import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.turret.{TurretUpgrade, WeaponTurret}
import net.psforever.objects.vehicles.MountedWeapons
+import net.psforever.objects.vital.DamageType
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.DamageWithPositionMessage
+import net.psforever.types.Vector3
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.vehicle.support.TurretUpgrader
-import net.psforever.services.vehicle.VehicleServiceMessage
+import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
/**
* The "control" `Actor` mixin for damage-handling code for `WeaponTurret` objects.
*/
-trait DamageableWeaponTurret extends DamageableEntity {
- def DamageableObject: Damageable.Target with WeaponTurret
+trait DamageableWeaponTurret
+ extends DamageableEntity
+ with AggravatedBehavior {
+ _: Actor =>
- override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Int): Unit = {
- super.DamageAwareness(target, cause, amount)
- if (amount > 0) {
- DamageableMountable.DamageAwareness(DamageableObject, cause)
+ def damageableWeaponTurretPostStop(): Unit = {
+ EndAllAggravation()
+ }
+
+ def DamageableObject: Damageable.Target with WeaponTurret
+ def AggravatedObject: Damageable.Target with WeaponTurret = DamageableObject
+
+ override val takesDamage: Receive = originalTakesDamage.orElse(aggravatedBehavior)
+
+ override protected def DamageAwareness(target: Damageable.Target, cause: ResolvedProjectile, amount: Any): Unit = {
+ val obj = DamageableObject
+ val zone = target.Zone
+ val events = zone.VehicleEvents
+ val targetGUID = target.GUID
+ val zoneId = zone.id
+ val damageToHealth = amount match {
+ case a: Int => a
+ case _ => 0
+ }
+ var announceConfrontation: Boolean = damageToHealth > 0
+ val aggravated = TryAggravationEffectActivate(cause) match {
+ case Some(_) =>
+ announceConfrontation = true
+ false
+ case _ =>
+ cause.projectile.profile.ProjectileDamageTypes.contains(DamageType.Aggravated)
+ }
+
+ //log historical event
+ target.History(cause)
+ //damage
+ if (Damageable.CanDamageOrJammer(target, damageToHealth, cause)) {
+ //jammering
+ if (Damageable.CanJammer(target, cause)) {
+ target.Actor ! JammableUnit.Jammered(cause)
+ }
+ //stat changes
+ //TODO some turrets have shields
+ if (damageToHealth > 0) {
+ DamageableMountable.DamageAwareness(DamageableObject, cause, damageToHealth)
+ events ! VehicleServiceMessage(
+ zoneId,
+ VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health)
+ )
+ announceConfrontation = true
+ }
+ }
+ if (announceConfrontation) {
+ if (aggravated) {
+ val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(damageToHealth, Vector3.Zero))
+ obj.Seats.values
+ .collect { case seat if seat.Occupant.nonEmpty => seat.Occupant.get.Name }
+ .foreach { channel =>
+ events ! VehicleServiceMessage(channel, msg)
+ }
+ }
+ else {
+ //activity on map
+ zone.Activity ! Zone.HotSpot.Activity(cause.target, cause.projectile.owner, cause.hit_pos)
+ //alert to damage source
+ DamageableMountable.DamageAwareness(obj, cause, damageToHealth)
+ }
}
}
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
super.DestructionAwareness(target, cause)
val obj = DamageableObject
+ EndAllAggravation()
DamageableWeaponTurret.DestructionAwareness(obj, cause)
DamageableMountable.DestructionAwareness(obj, cause)
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
index 799b339f..e4258dba 100644
--- a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala
@@ -88,9 +88,13 @@ class GeneratorControl(gen: Generator)
!imminentExplosion && super.WillAffectTarget(target, damage, cause)
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
+ override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
super.DamageAwareness(target, cause, amount)
- GeneratorControl.DamageAwareness(gen, cause, amount)
+ val damageTo = amount match {
+ case a: Int => a
+ case _ => 0
+ }
+ GeneratorControl.DamageAwareness(gen, cause, damageTo)
}
override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = {
diff --git a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
index ec5b7346..2bbb5ed4 100644
--- a/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/implantmech/ImplantTerminalMechControl.scala
@@ -72,9 +72,13 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
}
}
- override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Int): Unit = {
+ override protected def DamageAwareness(target: Target, cause: ResolvedProjectile, amount: Any): Unit = {
super.DamageAwareness(target, cause, amount)
- DamageableMountable.DamageAwareness(DamageableObject, cause)
+ val damageTo = amount match {
+ case a: Int => a
+ case _ => 0
+ }
+ DamageableMountable.DamageAwareness(DamageableObject, cause, damageTo)
}
override protected def DestructionAwareness(target: Damageable.Target, cause: ResolvedProjectile): Unit = {
diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
index 44a85f83..a7e3436c 100644
--- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala
@@ -44,6 +44,11 @@ class FacilityTurretControl(turret: FacilityTurret)
// Used for timing ammo recharge for vanu turrets in caves
var weaponAmmoRechargeTimer = Default.Cancellable
+ override def postStop(): Unit = {
+ super.postStop()
+ damageableWeaponTurretPostStop()
+ }
+
def receive: Receive =
checkBehavior
.orElse(jammableBehavior)
diff --git a/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index 1e8a4038..355698d4 100644
--- a/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -11,7 +11,7 @@ import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
-import net.psforever.objects.serverobject.damage.DamageableVehicle
+import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
import net.psforever.objects.serverobject.deploy.Deployment.DeploymentObject
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
import net.psforever.objects.serverobject.hackable.GenericHackables
@@ -51,11 +51,12 @@ class VehicleControl(vehicle: Vehicle)
with RepairableVehicle
with JammableMountedWeapons
with ContainableBehavior
- with AntTransferBehavior {
+ with AntTransferBehavior
+ with AggravatedBehavior {
//make control actors belonging to utilities when making control actor belonging to vehicle
vehicle.Utilities.foreach({ case (_, util) => util.Setup })
-
+
def MountableObject = vehicle
def CargoObject = vehicle
@@ -74,7 +75,7 @@ class VehicleControl(vehicle: Vehicle)
def ChargeTransferObject = vehicle
- if (vehicle.Definition == GlobalDefinitions.ant) {
+ if(vehicle.Definition == GlobalDefinitions.ant) {
findChargeTargetFunc = Vehicles.FindANTChargingSource
findDischargeTargetFunc = Vehicles.FindANTDischargingTarget
}
@@ -89,6 +90,7 @@ class VehicleControl(vehicle: Vehicle)
override def postStop(): Unit = {
super.postStop()
+ damageableVehiclePostStop()
decaying = false
decayTimer.cancel()
vehicle.Utilities.values.foreach { util =>
diff --git a/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala b/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
index 1fd9efc7..56140883 100644
--- a/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
+++ b/src/main/scala/net/psforever/objects/vital/DamageResistanceModel.scala
@@ -1,8 +1,8 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vital
-import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
import net.psforever.objects.vital.damage.DamageCalculations
+import net.psforever.objects.ballistics.ResolvedProjectile
import net.psforever.objects.vital.projectile.ProjectileCalculations
import net.psforever.objects.vital.resistance.ResistanceSelection
import net.psforever.objects.vital.resolution.ResolutionCalculations
@@ -72,7 +72,7 @@ trait DamageResistanceModel {
* @param resolution an explicit damage resolution overriding the one in the `ResolvedProjectile` object
* @return a function literal that encapsulates delayed modification instructions for certain objects
*/
- def Calculate(data: ResolvedProjectile, resolution: ProjectileResolution.Value): ResolutionCalculations.Output = {
+ def Calculate(data: ResolvedProjectile, resolution: DamageType.Value): ResolutionCalculations.Output = {
val res: ProjectileCalculations.Form = ResistUsing(resolution)
Model(DamageUsing, res, data)
}
diff --git a/src/main/scala/net/psforever/objects/vital/DamageType.scala b/src/main/scala/net/psforever/objects/vital/DamageType.scala
index c34a6ea2..5f5edb65 100644
--- a/src/main/scala/net/psforever/objects/vital/DamageType.scala
+++ b/src/main/scala/net/psforever/objects/vital/DamageType.scala
@@ -9,5 +9,5 @@ package net.psforever.objects.vital
object DamageType extends Enumeration(1) {
type Type = Value
- final val Direct, Splash, Radiation, Aggravated, Plasma, Comet, None = Value
+ final val Direct, Splash, Lash, Radiation, Aggravated, Plasma, Comet, None = Value
}
diff --git a/src/main/scala/net/psforever/objects/vital/StandardResistances.scala b/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
index 34b0e454..c8a5a74f 100644
--- a/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
+++ b/src/main/scala/net/psforever/objects/vital/StandardResistances.scala
@@ -6,70 +6,70 @@ import net.psforever.objects.vital.projectile.ProjectileCalculations
import net.psforever.objects.vital.resistance.{ResistanceCalculations, ResistanceSelection}
object NoResistance
- extends ResistanceCalculations[SourceEntry](
- ResistanceCalculations.ValidInfantryTarget,
- ResistanceCalculations.NoResistExtractor
- )
+ extends ResistanceCalculations[SourceEntry](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.NoResistExtractor
+ )
object InfantryHitResistance
- extends ResistanceCalculations[PlayerSource](
- ResistanceCalculations.ValidInfantryTarget,
- ResistanceCalculations.ExoSuitDirectExtractor
- )
+ extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.ExoSuitDirectExtractor
+ )
object InfantrySplashResistance
- extends ResistanceCalculations[PlayerSource](
- ResistanceCalculations.ValidInfantryTarget,
- ResistanceCalculations.ExoSuitSplashExtractor
- )
+ extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.ExoSuitSplashExtractor
+ )
object InfantryLashResistance
- extends ResistanceCalculations[PlayerSource](
- ResistanceCalculations.ValidInfantryTarget,
- ResistanceCalculations.MaximumResistance
- )
+ extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.MaximumResistance
+ )
object InfantryAggravatedResistance
- extends ResistanceCalculations[PlayerSource](
- ResistanceCalculations.ValidInfantryTarget,
- ResistanceCalculations.ExoSuitAggravatedExtractor
- )
+ extends ResistanceCalculations[PlayerSource](
+ ResistanceCalculations.ValidInfantryTarget,
+ ResistanceCalculations.ExoSuitAggravatedExtractor
+ )
object VehicleHitResistance
- extends ResistanceCalculations[VehicleSource](
- ResistanceCalculations.ValidVehicleTarget,
- ResistanceCalculations.VehicleDirectExtractor
- )
+ extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.VehicleDirectExtractor
+ )
object VehicleSplashResistance
- extends ResistanceCalculations[VehicleSource](
- ResistanceCalculations.ValidVehicleTarget,
- ResistanceCalculations.VehicleSplashExtractor
- )
+ extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.VehicleSplashExtractor
+ )
object VehicleLashResistance
- extends ResistanceCalculations[VehicleSource](
- ResistanceCalculations.ValidVehicleTarget,
- ResistanceCalculations.NoResistExtractor
- )
+ extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.NoResistExtractor
+ )
object VehicleAggravatedResistance
- extends ResistanceCalculations[VehicleSource](
- ResistanceCalculations.ValidVehicleTarget,
- ResistanceCalculations.VehicleAggravatedExtractor
- )
+ extends ResistanceCalculations[VehicleSource](
+ ResistanceCalculations.ValidVehicleTarget,
+ ResistanceCalculations.VehicleAggravatedExtractor
+ )
object AmenityHitResistance
- extends ResistanceCalculations[ObjectSource](
- ResistanceCalculations.ValidAmenityTarget,
- ResistanceCalculations.OtherDirectExtractor
- )
+ extends ResistanceCalculations[ObjectSource](
+ ResistanceCalculations.ValidAmenityTarget,
+ ResistanceCalculations.OtherDirectExtractor
+ )
-object AMenitySplashResistance
- extends ResistanceCalculations[ObjectSource](
- ResistanceCalculations.ValidAmenityTarget,
- ResistanceCalculations.OtherSplashExtractor
- )
+object AmenitySplashResistance
+ extends ResistanceCalculations[ObjectSource](
+ ResistanceCalculations.ValidAmenityTarget,
+ ResistanceCalculations.OtherSplashExtractor
+ )
object NoResistanceSelection extends ResistanceSelection {
def Direct: ProjectileCalculations.Form = None
diff --git a/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala b/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
index fb5eb164..673c7aea 100644
--- a/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
+++ b/src/main/scala/net/psforever/objects/vital/StandardResolutions.scala
@@ -11,13 +11,13 @@ object NoResolutions
object InfantryResolutions
extends DamageResistCalculations(
- ResolutionCalculations.InfantryDamageAfterResist,
+ ResolutionCalculations.InfantryDamage,
ResolutionCalculations.InfantryApplication
)
object MaxResolutions
extends DamageResistCalculations(
- ResolutionCalculations.MaxDamageAfterResist,
+ ResolutionCalculations.MaxDamage,
ResolutionCalculations.InfantryApplication
)
diff --git a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
index 478f5bfb..82a874ea 100644
--- a/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
+++ b/src/main/scala/net/psforever/objects/vital/damage/DamageModifiers.scala
@@ -1,8 +1,9 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.vital.damage
-import net.psforever.objects.ballistics.{ProjectileResolution, ResolvedProjectile}
-import net.psforever.types.Vector3
+import net.psforever.objects.ballistics._
+import net.psforever.objects.vital.DamageType
+import net.psforever.types.{ExoSuitType, Vector3}
/**
* Adjustments performed on the subsequent manipulations of the "base damage" value of an attack vector
@@ -45,6 +46,21 @@ object DamageModifiers {
private def function(damage: Int, data: ResolvedProjectile): Int = damage
}
+ case object MaxDistanceCutoff extends Mod {
+ def Calculate: DamageModifiers.Format = function
+
+ private def function(damage: Int, data: ResolvedProjectile): Int = {
+ val projectile = data.projectile
+ val profile = projectile.profile
+ val distance = Vector3.Distance(data.hit_pos, projectile.shot_origin)
+ if (distance <= profile.DistanceMax) {
+ damage
+ } else {
+ 0
+ }
+ }
+ }
+
/**
* The input value degrades (lessens)
* the further the distance between the point of origin (`shot_origin`)
@@ -81,14 +97,12 @@ object DamageModifiers {
def Calculate: DamageModifiers.Format = function
private def function(damage: Int, data: ResolvedProjectile): Int = {
- val projectile = data.projectile
- val profile = projectile.profile
+ val profile = data.projectile.profile
val distance = Vector3.Distance(data.hit_pos, data.target.Position)
val radius = profile.DamageRadius
if (distance <= radius) {
- val base: Float = profile.DamageAtEdge
- val degrade: Float = (1 - base) * ((radius - distance) / radius) + base
- (damage * degrade).toInt
+ val base: Float = profile.DamageAtEdge
+ (damage * ((1 - base) * ((radius - distance) / radius) + base)).toInt
} else {
0
}
@@ -117,4 +131,291 @@ object DamageModifiers {
}
}
}
+
+ /*
+ Below this point are the calculations for sources of aggravated damage.
+ For the most part, these calculations are individualistic and arbitrary.
+ They exist in their current form to satisfy observed shots to kill (STK) of specific weapon systems
+ according to 2012 standards of the Youtube video series by TheLegendaryNarwhal.
+ */
+ /**
+ * The initial application of aggravated damage against an infantry target
+ * where the specific damage component is `Direct`.
+ */
+ case object InfantryAggravatedDirect extends Mod {
+ def Calculate: DamageModifiers.Format =
+ BaseAggravatedFormula(ProjectileResolution.AggravatedDirect, DamageType.Direct)
+ }
+
+ /**
+ * The initial application of aggravated damage against an infantry target
+ * where the specific damage component is `Splash`.
+ */
+ case object InfantryAggravatedSplash extends Mod {
+ def Calculate: DamageModifiers.Format =
+ BaseAggravatedFormula(ProjectileResolution.AggravatedSplash, DamageType.Splash)
+ }
+
+ /**
+ * The ongoing application of aggravated damage ticks against an infantry target
+ * where the specific damage component is `Direct`.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ */
+ case object InfantryAggravatedDirectBurn extends Mod {
+ def Calculate: DamageModifiers.Format =
+ BaseAggravatedBurnFormula(ProjectileResolution.AggravatedDirectBurn, DamageType.Direct)
+ }
+
+ /**
+ * The ongoing application of aggravated damage ticks against an infantry target
+ * where the specific damage component is `Splash`.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ */
+ case object InfantryAggravatedSplashBurn extends Mod {
+ def Calculate: DamageModifiers.Format =
+ BaseAggravatedBurnFormula(ProjectileResolution.AggravatedSplashBurn, DamageType.Splash)
+ }
+
+ /**
+ * For damage application that involves aggravation of a particular damage type,
+ * calculate that initial damage application for infantry targets
+ * and produce the modified damage value.
+ * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
+ * @see `AggravatedDamage`
+ * @see `ExoSuitType`
+ * @see `InfantryAggravatedDirect`
+ * @see `InfantryAggravatedSplash`
+ * @see `PlayerSource`
+ * @see `ProjectileTarget.AggravatesTarget`
+ * @see `ResolvedProjectile`
+ * @param resolution the projectile resolution to match against
+ * @param damageType the damage type to find in as a component of aggravated information
+ * @param damage the base damage value
+ * @param data historical information related to the damage interaction
+ * @return the modified damage
+ */
+ private def BaseAggravatedFormula(
+ resolution: ProjectileResolution.Value,
+ damageType : DamageType.Value
+ )
+ (
+ damage: Int,
+ data: ResolvedProjectile
+ ): Int = {
+ if (data.resolution == resolution &&
+ data.projectile.quality == ProjectileQuality.AggravatesTarget) {
+ (data.projectile.profile.Aggravated, data.target) match {
+ case (Some(aggravation), p: PlayerSource) =>
+ val aggravatedDamage = aggravation.info.find(_.damage_type == damageType) match {
+ case Some(infos) =>
+ damage * infos.degradation_percentage + damage
+ case _ =>
+ damage toFloat
+ }
+ if(p.ExoSuit == ExoSuitType.MAX) {
+ (aggravatedDamage * aggravation.max_factor) toInt
+ } else {
+ aggravatedDamage toInt
+ }
+ case _ =>
+ damage
+ }
+ } else {
+ damage
+ }
+ }
+
+ /**
+ * For damage application that involves aggravation of a particular damage type,
+ * calculate that damage application burn for each tick for infantry targets
+ * and produce the modified damage value.
+ * Infantry wearing mechanized assault exo-suits (MAX) incorporate an additional modifier.
+ * Vanilla infantry incorporate their resistance value into a slightly different calculation than usual.
+ * @see `AggravatedDamage`
+ * @see `ExoSuitType`
+ * @see `InfantryAggravatedDirectBurn`
+ * @see `InfantryAggravatedSplashBurn`
+ * @see `PlayerSource`
+ * @see `ResolvedProjectile`
+ * @param resolution the projectile resolution to match against
+ * @param damageType the damage type to find in as a component of aggravated information
+ * @param damage the base damage value
+ * @param data historical information related to the damage interaction
+ * @return the modified damage
+ */
+ private def BaseAggravatedBurnFormula(
+ resolution: ProjectileResolution.Value,
+ damageType : DamageType.Value
+ )
+ (
+ damage: Int,
+ data: ResolvedProjectile
+ ): Int = {
+ if (data.resolution == resolution) {
+ (data.projectile.profile.Aggravated, data.target) match {
+ case (Some(aggravation), p: PlayerSource) =>
+ val degradation = aggravation.info.find(_.damage_type == damageType) match {
+ case Some(info) =>
+ info.degradation_percentage
+ case _ =>
+ 1f
+ }
+ if (p.exosuit == ExoSuitType.MAX) {
+ (damage * degradation * aggravation.max_factor) toInt
+ } else {
+ val resist = data.damage_model.ResistUsing(data)(data)
+ //add resist to offset resist subtraction later
+ if (damage > resist) {
+ ((damage - resist) * degradation).toInt + resist
+ } else {
+ (damage * degradation).toInt + resist
+ }
+ }
+ case _ =>
+ 0
+ }
+ } else {
+ damage
+ }
+ }
+
+ /**
+ * For damage application that involves aggravation of a fireball (Dragon secondary fire mode),
+ * perform 1 damage.
+ * @see `ResolvedProjectile`
+ */
+ case object FireballAggravatedBurn extends Mod {
+ def Calculate: DamageModifiers.Format = formula
+
+ private def formula(damage: Int, data: ResolvedProjectile): Int = {
+ if (damage > 0 &&
+ data.resolution == ProjectileResolution.AggravatedDirectBurn ||
+ data.resolution == ProjectileResolution.AggravatedSplashBurn) {
+ //add resist to offset resist subtraction later
+ 1 + data.damage_model.ResistUsing(data)(data)
+ } else {
+ damage
+ }
+ }
+ }
+
+ /**
+ * The initial application of aggravated damage against an aircraft target.
+ * Primarily for use in the starfire weapon system.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality.AggravatesTarget`
+ * @see `ResolvedProjectile`
+ */
+ case object StarfireAggravated extends Mod {
+ def Calculate: DamageModifiers.Format = formula
+
+ private def formula(damage: Int, data: ResolvedProjectile): Int = {
+ if (data.resolution == ProjectileResolution.AggravatedDirect &&
+ data.projectile.quality == ProjectileQuality.AggravatesTarget) {
+ data.projectile.profile.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ (damage * infos.degradation_percentage + damage) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ damage
+ }
+ } else {
+ damage
+ }
+ }
+ }
+
+ /**
+ * The ongoing application of aggravated damage ticks against an aircraft target.
+ * Primarily for use in the starfire weapon system.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality`
+ * @see `ResolvedProjectile`
+ */
+ case object StarfireAggravatedBurn extends Mod {
+ def Calculate: DamageModifiers.Format = formula
+
+ private def formula(damage: Int, data: ResolvedProjectile): Int = {
+ if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
+ data.projectile.profile.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ (math.floor(damage * infos.degradation_percentage) * data.projectile.quality.mod) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ 0
+ }
+ } else {
+ damage
+ }
+ }
+ }
+
+ /**
+ * The initial application of aggravated damage against a target.
+ * Primarily for use in the comet weapon system.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality.AggravatesTarget`
+ * @see `ResolvedProjectile`
+ */
+ case object CometAggravated extends Mod {
+ def Calculate: DamageModifiers.Format = formula
+
+ private def formula(damage: Int, data: ResolvedProjectile): Int = {
+ if (data.resolution == ProjectileResolution.AggravatedDirect &&
+ data.projectile.quality == ProjectileQuality.AggravatesTarget) {
+ data.projectile.profile.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ damage - (damage * infos.degradation_percentage) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ damage
+ }
+ } else {
+ damage
+ }
+ }
+ }
+
+ /**
+ * The ongoing application of aggravated damage ticks against a target.
+ * Primarily for use in the comet weapon system.
+ * This is called "burning" regardless of what the active aura effect actually is.
+ * @see `AggravatedDamage`
+ * @see `ProjectileQuality`
+ * @see `ResolvedProjectile`
+ */
+ case object CometAggravatedBurn extends Mod {
+ def Calculate: DamageModifiers.Format = formula
+
+ private def formula(damage: Int, data: ResolvedProjectile): Int = {
+ if (data.resolution == ProjectileResolution.AggravatedDirectBurn) {
+ data.projectile.profile.Aggravated match {
+ case Some(aggravation) =>
+ aggravation.info.find(_.damage_type == DamageType.Direct) match {
+ case Some(infos) =>
+ damage - (damage * infos.degradation_percentage) toInt
+ case _ =>
+ damage
+ }
+ case _ =>
+ 0
+ }
+ } else {
+ damage
+ }
+ }
+ }
}
diff --git a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
index ea889e1b..0e7c466d 100644
--- a/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
+++ b/src/main/scala/net/psforever/objects/vital/resistance/ResistanceSelection.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.vital.resistance
import net.psforever.objects.ballistics._
-import net.psforever.objects.vital.NoResistance
+import net.psforever.objects.vital.{DamageType, NoResistance}
import net.psforever.objects.vital.projectile.ProjectileCalculations
/**
@@ -17,19 +17,19 @@ trait ResistanceSelection {
def Lash: ProjectileCalculations.Form
def Aggravated: ProjectileCalculations.Form
- def apply(data: ResolvedProjectile): ProjectileCalculations.Form =
- data.resolution match {
- case ProjectileResolution.Hit => Direct
- case ProjectileResolution.Splash => Splash
- case ProjectileResolution.Lash => Lash
- case _ => None
- }
+ def apply(data : ResolvedProjectile) : ProjectileCalculations.Form = data.projectile.profile.ProjectileDamageType match {
+ case DamageType.Direct => Direct
+ case DamageType.Splash => Splash
+ case DamageType.Lash => Lash
+ case DamageType.Aggravated => Aggravated
+ case _ => None
+ }
- def apply(res: ProjectileResolution.Value): ProjectileCalculations.Form =
- res match {
- case ProjectileResolution.Hit => Direct
- case ProjectileResolution.Splash => Splash
- case ProjectileResolution.Lash => Lash
- case _ => None
- }
+ def apply(res : DamageType.Value) : ProjectileCalculations.Form = res match {
+ case DamageType.Direct => Direct
+ case DamageType.Splash => Splash
+ case DamageType.Lash => Lash
+ case DamageType.Aggravated => Aggravated
+ case _ => None
+ }
}
diff --git a/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala b/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
index 5067a74e..9166f7d0 100644
--- a/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
+++ b/src/main/scala/net/psforever/objects/vital/resolution/ResolutionCalculations.scala
@@ -38,15 +38,31 @@ object ResolutionCalculations {
def NoDamage(data: ResolvedProjectile)(a: Int, b: Int): Int = 0
- def InfantryDamageAfterResist(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
+ def InfantryDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
data.target match {
case target: PlayerSource =>
- InfantryDamageAfterResist(target.health, target.armor)
+ if(data.projectile.profile.DamageToHealthOnly) {
+ DamageToHealthOnly(target.health)
+ } else {
+ InfantryDamageAfterResist(target.health, target.armor)
+ }
case _ =>
InfantryDamageAfterResist(0, 0)
}
}
+ def DamageToHealthOnly(currentHP: Int)(damages: Int, resistance: Int): (Int, Int) = {
+ if (damages > 0 && currentHP > 0) {
+ if(damages > resistance) {
+ (damages - resistance, 0)
+ } else {
+ (damages, 0)
+ }
+ } else {
+ (0, 0)
+ }
+ }
+
def InfantryDamageAfterResist(currentHP: Int, currentArmor: Int)(damages: Int, resistance: Int): (Int, Int) = {
if (damages > 0 && currentHP > 0) {
if (currentArmor <= 0) {
@@ -67,10 +83,14 @@ object ResolutionCalculations {
}
}
- def MaxDamageAfterResist(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
+ def MaxDamage(data: ResolvedProjectile): (Int, Int) => (Int, Int) = {
data.target match {
case target: PlayerSource =>
- MaxDamageAfterResist(target.health, target.armor)
+ if(data.projectile.profile.DamageToHealthOnly) {
+ DamageToHealthOnly(target.health)
+ } else {
+ MaxDamageAfterResist(target.health, target.armor)
+ }
case _ =>
MaxDamageAfterResist(0, 0)
}
diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala
index 5f17e46b..debbf627 100644
--- a/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -451,7 +451,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
* `false`, if the new pool can not be created because the system has already been started
*/
def AddPool(name: String, pool: Seq[Int]): Boolean = {
- if (accessor == Default.Actor) {
+ if (accessor == Default.Actor || accessor == null) {
guid.AddPool(name, pool.toList)
true
} else {
diff --git a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index b77e183b..a3a7256a 100644
--- a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -255,7 +255,7 @@ object GamePacketOpcode extends Enumeration {
// 0x68
case 0x68 => game.DroppodFreefallingMessage.decode
case 0x69 => game.AvatarFirstTimeEventMessage.decode
- case 0x6a => noDecoder(AggravatedDamageMessage)
+ case 0x6a => game.AggravatedDamageMessage.decode
case 0x6b => game.TriggerSoundMessage.decode
case 0x6c => game.LootItemMessage.decode
case 0x6d => game.VehicleSubStateMessage.decode
diff --git a/src/main/scala/net/psforever/packet/game/AggravatedDamageMessage.scala b/src/main/scala/net/psforever/packet/game/AggravatedDamageMessage.scala
new file mode 100644
index 00000000..2456bee2
--- /dev/null
+++ b/src/main/scala/net/psforever/packet/game/AggravatedDamageMessage.scala
@@ -0,0 +1,32 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
+import net.psforever.types.PlanetSideGUID
+import scodec.Codec
+import scodec.codecs._
+
+/**
+ * Dispatched from the server to cause a damage reaction from a specific target.
+ * Infantry targets should be the primary target of this packet, as indicated by their identifier.
+ * Infantry targets display their flinch animation.
+ * All targets yelp in agony.
+ * Infantry targets use their assigned voice.
+ * Non-infantry targets use the "grizzled"(?) voice.
+ * @param guid the target entity's global unique identifier
+ * @param damage the amount of damsge being simulated
+ */
+final case class AggravatedDamageMessage(guid : PlanetSideGUID,
+ damage : Long)
+ extends PlanetSideGamePacket {
+ type Packet = AggravatedDamageMessage
+ def opcode = GamePacketOpcode.AggravatedDamageMessage
+ def encode = AggravatedDamageMessage.encode(this)
+}
+
+object AggravatedDamageMessage extends Marshallable[AggravatedDamageMessage] {
+ implicit val codec : Codec[AggravatedDamageMessage] = (
+ ("guid" | PlanetSideGUID.codec) ::
+ ("damage" | uint32L)
+ ).as[AggravatedDamageMessage]
+}
diff --git a/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala b/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
index 181a67b2..f88bcf03 100644
--- a/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
@@ -19,7 +19,7 @@ import shapeless.{::, HNil}
* @param unk3b if no global unique identifier (above), the name of the entity absorbing the damage
* @param unk3c if no global unique identifier (above), the object type of the entity absorbing the damage
* @param unk3d na
- * @param unk4 na
+ * @param unk4 an indicator for the target-specific vital statistic being affected
* @param unk5 the amount of damage
* @param unk6 na
*/
@@ -66,6 +66,13 @@ final case class DamageFeedbackMessage(
}
object DamageFeedbackMessage extends Marshallable[DamageFeedbackMessage] {
+ def apply(unk1: Int,
+ unk2: PlanetSideGUID,
+ unk3: PlanetSideGUID,
+ unk4: Int,
+ unk5: Long): DamageFeedbackMessage =
+ DamageFeedbackMessage(unk1, true, Some(unk2), None, None, true, Some(unk3), None, None, None, unk4, unk5, 0)
+
implicit val codec: Codec[DamageFeedbackMessage] = (
("unk1" | uint4) ::
(bool >>:~ { u2 =>
diff --git a/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala b/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
index 8f2a09e1..d6e4a2df 100644
--- a/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/DamageWithPositionMessage.scala
@@ -5,10 +5,13 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacke
import net.psforever.types.Vector3
import scodec.Codec
import scodec.codecs._
+import shapeless.{::, HNil}
/**
* Dispatched by the server to indicate a source of damage affecting the player.
- * Unlike `HitHint` the damage source is defined by an actual coordinate location rather than a physical target.
+ * Unlike `HitHint` the damage source is defined by an actual coordinate location rather than a physical target.
+ * Setting the position to the world origin, however,
+ * can cause the damage tick mark to point towards the previous damaging entity in some situations.
*
* The player will be shown a fading, outwards drifting, red tick mark.
* The location will indicate a general direction towards the source.
@@ -27,5 +30,14 @@ object DamageWithPositionMessage extends Marshallable[DamageWithPositionMessage]
implicit val codec: Codec[DamageWithPositionMessage] = (
("unk" | uint8L) ::
("pos" | Vector3.codec_pos)
- ).as[DamageWithPositionMessage]
+ ).xmap[DamageWithPositionMessage] (
+ {
+ case unk :: pos :: HNil =>
+ DamageWithPositionMessage(math.max(0, math.min(unk, 255)), pos)
+ },
+ {
+ case DamageWithPositionMessage(unk, pos) =>
+ unk :: pos :: HNil
+ }
+ )
}
diff --git a/src/test/scala/game/AggravatedDamageMessageTest.scala b/src/test/scala/game/AggravatedDamageMessageTest.scala
new file mode 100644
index 00000000..7cc9783a
--- /dev/null
+++ b/src/test/scala/game/AggravatedDamageMessageTest.scala
@@ -0,0 +1,29 @@
+// Copyright (c) 2020 PSForever
+package game
+
+import net.psforever.packet._
+import net.psforever.packet.game._
+import net.psforever.types.PlanetSideGUID
+import org.specs2.mutable._
+import scodec.bits._
+
+class AggravatedDamageMessageTest extends Specification {
+ val string = hex"6a350a0e000000"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case AggravatedDamageMessage(guid,unk) =>
+ guid mustEqual PlanetSideGUID(2613)
+ unk mustEqual 14
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = AggravatedDamageMessage(PlanetSideGUID(2613), 14)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+}
diff --git a/src/test/scala/objects/AuraTest.scala b/src/test/scala/objects/AuraTest.scala
new file mode 100644
index 00000000..40aec57c
--- /dev/null
+++ b/src/test/scala/objects/AuraTest.scala
@@ -0,0 +1,285 @@
+// Copyright (c) 2020 PSForever
+package objects
+
+import akka.actor.{Actor, ActorRef, Props}
+import akka.testkit.TestProbe
+import base.ActorTest
+import net.psforever.objects.serverobject.PlanetSideServerObject
+import net.psforever.objects.serverobject.aura.AuraEffectBehavior.Target
+import net.psforever.objects.serverobject.aura.{Aura, AuraContainer, AuraEffectBehavior}
+import net.psforever.types.PlanetSideEmpire
+import org.specs2.mutable.Specification
+
+import scala.concurrent.duration._
+
+class AuraContainerTest extends Specification {
+ "AuraContainer" should {
+ "have no default effects" in {
+ new AuraTest.Entity().Aura.isEmpty mustEqual true
+ }
+
+ "add effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ }
+
+ "do nothing if adding repeated effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ }
+
+ "remove effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ obj.RemoveEffectFromAura(Aura.Plasma)
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ }
+
+ "do nothing if no effects" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.RemoveEffectFromAura(Aura.Plasma)
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ }
+
+ "do nothing if trying to remove wrong effect" in {
+ val obj = new AuraTest.Entity()
+ obj.Aura.size mustEqual 0
+ obj.Aura.contains(Aura.Plasma) mustEqual false
+ obj.Aura.contains(Aura.Fire) mustEqual false
+ obj.AddEffectToAura(Aura.Plasma)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ obj.Aura.contains(Aura.Fire) mustEqual false
+ obj.RemoveEffectFromAura(Aura.Fire)
+ obj.Aura.size mustEqual 1
+ obj.Aura.contains(Aura.Plasma) mustEqual true
+ obj.Aura.contains(Aura.Fire) mustEqual false
+ }
+ }
+}
+
+class AuraEffectBehaviorInitTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+
+ "AuraEffectBehavior" should {
+ "init" in {
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, ActorRef.noSender), "aura-test-actor")
+ expectNoMessage(500 milliseconds)
+ }
+ }
+}
+
+class AuraEffectBehaviorStartEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "start effect (ends naturally)" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ expectNoMessage(2000 milliseconds)
+ assert(obj.Aura.contains(Aura.Plasma))
+ val msg2 = updateProbe.receiveOne(750 milliseconds)
+ assert(
+ msg2 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.isEmpty)
+ }
+ }
+}
+
+class AuraEffectBehaviorStartLongerEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "replace a shorter effect with a longer one" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ updateProbe.expectNoMessage(2000 milliseconds)
+ //first effect has not ended naturally (yet)
+ assert(obj.Aura.contains(Aura.Plasma))
+ }
+ }
+}
+
+class AuraEffectBehaviorNoRedundantStartEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "not start an effect if already active" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ expectNoMessage(1000 milliseconds) //wait for half of the effect's duration
+ assert(obj.Aura.contains(Aura.Plasma))
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ updateProbe.expectNoMessage(1500 milliseconds)
+ }
+ }
+}
+
+class AuraEffectBehaviorNoOverrideStartEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "not replace a long-running effect with a short-running effect" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 500)
+ updateProbe.expectNoMessage(1500 milliseconds)
+ //effect has not ended naturally
+ assert(obj.Aura.contains(Aura.Plasma))
+ }
+ }
+}
+
+class AuraEffectBehaviorNoStartUnsupportedEffectTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor") //supports Plasma only
+
+ "AuraEffectBehavior" should {
+ "not start an effect that is not approved" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Fire, 2500)
+ assert(obj.Aura.isEmpty)
+ updateProbe.expectNoMessage(2000 milliseconds)
+ }
+ }
+}
+
+
+
+class AuraEffectBehaviorEndEarlyTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "start effect (ends early)" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.contains(Aura.Plasma))
+ obj.Actor ! AuraEffectBehavior.EndEffect(Aura.Plasma)
+ val msg2 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg2 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.isEmpty)
+ }
+ }
+}
+
+class AuraEffectBehaviorEndNothingTest extends ActorTest {
+ val obj = new AuraTest.Entity()
+ val updateProbe = new TestProbe(system)
+ obj.Actor = system.actorOf(Props(classOf[AuraTest.Agency], obj, updateProbe.ref), "aura-test-actor")
+
+ "AuraEffectBehavior" should {
+ "can not end an effect that is not supported (hence, not started)" in {
+ assert(obj.Aura.isEmpty)
+ obj.Actor ! AuraEffectBehavior.StartEffect(Aura.Plasma, 2500)
+ val msg1 = updateProbe.receiveOne(100 milliseconds)
+ assert(
+ msg1 match {
+ case AuraTest.DoUpdateAuraEffect() => true
+ case _ => false
+ }
+ )
+ assert(obj.Aura.size == 1)
+ obj.Actor ! AuraEffectBehavior.EndEffect(Aura.Fire)
+ updateProbe.expectNoMessage(1000 milliseconds)
+ assert(obj.Aura.size == 1)
+ }
+ }
+}
+
+object AuraTest {
+ class Agency(obj: AuraEffectBehavior.Target, updateRef: ActorRef) extends Actor with AuraEffectBehavior {
+ def AuraTargetObject : Target = obj
+ ApplicableEffect(Aura.Plasma)
+
+ def receive: Receive = auraBehavior.orElse {
+ case _ => ;
+ }
+
+ def UpdateAuraEffect(target : Target) : Unit = {
+ updateRef ! DoUpdateAuraEffect()
+ }
+ }
+
+ class Entity extends PlanetSideServerObject with AuraContainer {
+ def Faction = PlanetSideEmpire.NEUTRAL
+ def Definition = null
+ }
+
+ final case class DoUpdateAuraEffect()
+}
diff --git a/src/test/scala/objects/DamageModelTests.scala b/src/test/scala/objects/DamageModelTests.scala
index 06f461a3..656e91fc 100644
--- a/src/test/scala/objects/DamageModelTests.scala
+++ b/src/test/scala/objects/DamageModelTests.scala
@@ -28,7 +28,7 @@ class DamageCalculationsTests extends Specification {
val target = Vehicle(GlobalDefinitions.fury)
target.Position = Vector3(10, 0, 0)
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target),
target.DamageModel,
@@ -64,14 +64,7 @@ class DamageCalculationsTests extends Specification {
}
"degrade over distance damage modifier (no degrade)" in {
- val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
- projectile,
- SourceEntry(target),
- target.DamageModel,
- Vector3(10, 0, 0)
- )
- DamageModifiers.DistanceDegrade.Calculate(100, resprojectile2) == 100 mustEqual true
+ DamageModifiers.DistanceDegrade.Calculate(100, resprojectile) == 100 mustEqual true
}
"degrade over distance damage modifier (some degrade)" in {
@@ -109,7 +102,14 @@ class DamageCalculationsTests extends Specification {
}
"degrade at radial distance damage modifier (some degrade)" in {
- val damage = DamageModifiers.RadialDegrade.Calculate(100, resprojectile)
+ val resprojectile2 = ResolvedProjectile(
+ ProjectileResolution.Splash,
+ projectile,
+ SourceEntry(target),
+ target.DamageModel,
+ Vector3(12, 0, 0)
+ )
+ val damage = DamageModifiers.RadialDegrade.Calculate(100, resprojectile2)
damage < 100 && damage > 0 mustEqual true
}
@@ -119,7 +119,7 @@ class DamageCalculationsTests extends Specification {
projectile,
SourceEntry(target),
target.DamageModel,
- Vector3(1000, 0, 0)
+ Vector3(100, 0, 0)
)
DamageModifiers.RadialDegrade.Calculate(100, resprojectile2) == 0 mustEqual true
}
@@ -180,7 +180,7 @@ class ResistanceCalculationsTests extends Specification {
"ignore all targets" in {
val target = Vehicle(GlobalDefinitions.fury)
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target),
target.DamageModel,
@@ -192,7 +192,7 @@ class ResistanceCalculationsTests extends Specification {
"discern standard infantry targets" in {
val target = player
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target),
target.DamageModel,
@@ -208,7 +208,7 @@ class ResistanceCalculationsTests extends Specification {
val target = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
target.ExoSuit = ExoSuitType.MAX
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target),
target.DamageModel,
@@ -223,7 +223,7 @@ class ResistanceCalculationsTests extends Specification {
"discern ground vehicle targets" in {
val target = Vehicle(GlobalDefinitions.fury)
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target),
target.DamageModel,
@@ -238,7 +238,7 @@ class ResistanceCalculationsTests extends Specification {
"discern flying vehicle targets" in {
val target = Vehicle(GlobalDefinitions.mosquito)
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target),
target.DamageModel,
@@ -284,7 +284,7 @@ class ResolutionCalculationsTests extends Specification {
"calculate no damage" in {
val target = player
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target),
target.DamageModel,
@@ -296,23 +296,23 @@ class ResolutionCalculationsTests extends Specification {
"calculate no infantry damage for vehicles" in {
val target1 = Vehicle(GlobalDefinitions.fury) //!
val resprojectile1 = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target1),
target1.DamageModel,
Vector3.Zero
)
- InfantryDamageAfterResist(resprojectile1)(50, 10) mustEqual (0, 0)
+ InfantryDamage(resprojectile1)(50, 10) mustEqual (0, 0)
val target2 = player
val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target2),
target2.DamageModel,
Vector3.Zero
)
- InfantryDamageAfterResist(resprojectile2)(50, 10) mustEqual (40, 10)
+ InfantryDamage(resprojectile2)(50, 10) mustEqual (40, 10)
}
"calculate health and armor damage for infantry target" in {
@@ -340,23 +340,23 @@ class ResolutionCalculationsTests extends Specification {
"calculate no max damage for vehicles" in {
val target1 = Vehicle(GlobalDefinitions.fury) //!
val resprojectile1 = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target1),
target1.DamageModel,
Vector3.Zero
)
- MaxDamageAfterResist(resprojectile1)(50, 10) mustEqual (0, 0)
+ MaxDamage(resprojectile1)(50, 10) mustEqual (0, 0)
val target2 = player2
val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target2),
target2.DamageModel,
Vector3.Zero
)
- MaxDamageAfterResist(resprojectile2)(50, 10) mustEqual (0, 40)
+ MaxDamage(resprojectile2)(50, 10) mustEqual (0, 40)
}
"calculate health and armor damage for max target" in {
@@ -376,7 +376,7 @@ class ResolutionCalculationsTests extends Specification {
"do not care if target is infantry for vehicle calculations" in {
val target1 = player
val resprojectile1 = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target1),
target1.DamageModel,
@@ -386,7 +386,7 @@ class ResolutionCalculationsTests extends Specification {
val target2 = Vehicle(GlobalDefinitions.fury) //!
val resprojectile2 = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(target2),
target2.DamageModel,
@@ -450,10 +450,9 @@ class DamageModelTests extends Specification {
Vector3.Zero
)
val func: Any => ResolvedProjectile = resprojectile.damage_model.Calculate(resprojectile)
-
func(tplayer)
- tplayer.Health mustEqual 54
- tplayer.Armor mustEqual 46
+ tplayer.Health mustEqual 65
+ tplayer.Armor mustEqual 35
}
"resolve infantry targets in a specific way" in {
@@ -471,8 +470,7 @@ class DamageModelTests extends Specification {
Vector3.Zero
)
val func: Any => ResolvedProjectile =
- resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
-
+ resprojectile.damage_model.Calculate(resprojectile, DamageType.Splash)
func(tplayer)
tplayer.Health mustEqual 65
tplayer.Armor mustEqual 35
@@ -572,7 +570,7 @@ class DamageModelTests extends Specification {
Vector3.Zero
)
val func: Any => ResolvedProjectile =
- resprojectile.damage_model.Calculate(resprojectile, ProjectileResolution.Splash)
+ resprojectile.damage_model.Calculate(resprojectile, DamageType.Splash)
func(vehicle)
vehicle.Health mustEqual 518
diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala
index 6cce2e8b..d5973874 100644
--- a/src/test/scala/objects/DamageableTest.scala
+++ b/src/test/scala/objects/DamageableTest.scala
@@ -40,7 +40,7 @@ class DamageableTest extends Specification {
"permit damage" in {
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -53,7 +53,7 @@ class DamageableTest extends Specification {
"ignore attempts at non-zero damage" in {
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectileA,
weaponA.Definition,
@@ -78,7 +78,7 @@ class DamageableTest extends Specification {
Faction = player1.Faction
}
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -101,7 +101,7 @@ class DamageableTest extends Specification {
Faction = PlanetSideEmpire.NC
}
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -128,7 +128,7 @@ class DamageableTest extends Specification {
Faction = player1.Faction
}
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -152,7 +152,7 @@ class DamageableTest extends Specification {
"permit jamming" in {
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -166,7 +166,7 @@ class DamageableTest extends Specification {
"ignore attempts at jamming if the projectile is does not cause the effect" in {
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileA, weaponA.Definition, weaponA.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -181,7 +181,7 @@ class DamageableTest extends Specification {
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
target.Faction = player1.Faction
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -196,7 +196,7 @@ class DamageableTest extends Specification {
"ignore attempts at jamming targets that are not jammable" in {
val target = new TrapDeployable(GlobalDefinitions.tank_traps)
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -216,7 +216,7 @@ class DamageableTest extends Specification {
val target = new SensorDeployable(GlobalDefinitions.motionalarmsensor)
target.Faction = player1.Faction
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectileB, weaponB.Definition, weaponB.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
SourceEntry(target),
target.DamageModel,
@@ -271,7 +271,7 @@ class DamageableEntityDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -342,7 +342,7 @@ class DamageableEntityDestroyedTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -416,7 +416,7 @@ class DamageableEntityNotDestroyTwice extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -487,7 +487,7 @@ class DamageableAmenityTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -580,7 +580,7 @@ class DamageableMountableDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -626,7 +626,7 @@ class DamageableMountableDamageTest extends ActorTest {
msg1_3(1) match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 2, 2)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2)))
) =>
true
case _ => false
@@ -675,7 +675,7 @@ class DamageableMountableDestroyTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -737,8 +737,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
}
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
+ val vehicleProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
+ zone.VehicleEvents = vehicleProbe.ref
val turret = new TurretDeployable(GlobalDefinitions.portable_manned_turret_tr) //2
turret.Actor = system.actorOf(Props(classOf[TurretControl], turret), "turret-control")
turret.Zone = zone
@@ -787,16 +789,17 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
assert(turret.Health == turret.Definition.DefaultHealth)
turret.Actor ! Vitality.Damage(applyDamageTo)
- val msg1_3 = avatarProbe.receiveN(2, 500 milliseconds)
- val msg2 = activityProbe.receiveOne(500 milliseconds)
+ val msg12 = vehicleProbe.receiveOne(500 milliseconds)
+ val msg3 = activityProbe.receiveOne(500 milliseconds)
+ val msg4 = avatarProbe.receiveOne(500 milliseconds)
assert(
- msg1_3.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
- case _ => false
+ msg12 match {
+ case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(2), 0, _)) => true
+ case _ => false
}
)
assert(
- msg2 match {
+ msg3 match {
case activity: Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
activity.defender == turretSource &&
@@ -805,10 +808,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
}
)
assert(
- msg1_3(1) match {
+ msg4 match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 2, 2)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2)))
) =>
true
case _ => false
@@ -862,7 +865,7 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
val projectile = weapon.Projectile
val turretSource = SourceEntry(turret)
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -964,7 +967,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
val weaponA = Tool(GlobalDefinitions.jammer_grenade)
val projectileA = weaponA.Projectile
val resolvedA = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectileA,
weaponA.Definition,
@@ -983,7 +986,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
val projectileB = weaponB.Projectile
val resolvedB = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectileB,
weaponB.Definition,
@@ -1046,8 +1049,8 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
)
assert(
msg56.head match {
- case VehicleServiceMessage.TurretUpgrade(SupportActor.ClearSpecific(List(t), _)) if t eq turret => true
- case _ => false
+ case VehicleServiceMessage.TurretUpgrade(SupportActor.ClearSpecific(List(t), _)) if turret eq t => true
+ case _ => false
}
)
assert(
@@ -1130,40 +1133,36 @@ class DamageableVehicleDamageTest extends ActorTest {
assert(atv.Shields == 1)
atv.Actor ! Vitality.Damage(applyDamageTo)
- val msg1_3 = avatarProbe.receiveN(2, 500 milliseconds)
- val msg2 = activityProbe.receiveOne(200 milliseconds)
- val msg4 = vehicleProbe.receiveOne(200 milliseconds)
+ val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
+ val msg3 = activityProbe.receiveOne(200 milliseconds)
+ val msg4 = avatarProbe.receiveOne(200 milliseconds)
assert(
- msg1_3.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
- case _ => false
+ msg12.head match {
+ case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
+ case _ => false
}
)
assert(
- msg2 match {
+ msg12(1) match {
+ case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
+ case _ => false
+ }
+ )
+ assert(
+ msg3 match {
case activity: Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
- activity.defender == vehicleSource &&
- activity.location == Vector3(1, 0, 0)
- case _ => false
- }
- )
- assert(
- msg1_3(1) match {
- case AvatarServiceMessage(
- "TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
- ) =>
- true
+ activity.defender == vehicleSource &&
+ activity.location == Vector3(1, 0, 0)
case _ => false
}
)
assert(
msg4 match {
- case VehicleServiceMessage(
- channel,
- VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)
- ) if channel.equals(atv.Actor.toString) =>
+ case AvatarServiceMessage(
+ "TestCharacter2",
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(9, Vector3(2, 0, 0)))
+ ) =>
true
case _ => false
}
@@ -1264,17 +1263,23 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
assert(atv.Shields == 1)
lodestar.Actor ! Vitality.Damage(applyDamageTo)
- val msg1_35 = avatarProbe.receiveN(3, 500 milliseconds)
- val msg2 = activityProbe.receiveOne(200 milliseconds)
- val msg4 = vehicleProbe.receiveOne(200 milliseconds)
+ val msg12 = vehicleProbe.receiveN(2, 200 milliseconds)
+ val msg3 = activityProbe.receiveOne(200 milliseconds)
+ val msg45 = avatarProbe.receiveN(2,200 milliseconds)
assert(
- msg1_35.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
- case _ => false
+ msg12.head match {
+ case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true
+ case _ => false
}
)
assert(
- msg2 match {
+ msg12(1) match {
+ case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true
+ case _ => false
+ }
+ )
+ assert(
+ msg3 match {
case activity: Zone.HotSpot.Activity =>
activity.attacker == PlayerSource(player1) &&
activity.defender == vehicleSource &&
@@ -1283,30 +1288,20 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
}
)
assert(
- msg1_35(1) match {
+ msg45.head match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(400, Vector3(2, 0, 0)))
) =>
true
case _ => false
}
)
assert(
- msg4 match {
- case VehicleServiceMessage(
- channel,
- VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)
- ) if channel.equals(lodestar.Actor.toString) =>
- true
- case _ => false
- }
- )
- assert(
- msg1_35(2) match {
+ msg45(1) match {
case AvatarServiceMessage(
"TestCharacter3",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(0, Vector3(2, 0, 0)))
) =>
true
case _ => false
@@ -1385,7 +1380,7 @@ class DamageableVehicleJammeringMountedTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.jammer_grenade)
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -1601,7 +1596,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
val weaponA = Tool(GlobalDefinitions.jammer_grenade)
val projectileA = weaponA.Projectile
val resolvedA = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectileA,
weaponA.Definition,
@@ -1620,7 +1615,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
val projectileB = weaponB.Projectile
val resolvedB = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectileB,
weaponB.Definition,
diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala
index e96e4cfe..39ab6107 100644
--- a/src/test/scala/objects/DeployableTest.scala
+++ b/src/test/scala/objects/DeployableTest.scala
@@ -334,7 +334,7 @@ class ExplosiveDeployableJammerTest extends ActorTest {
val pSource = PlayerSource(player1)
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
jMineSource,
j_mine.DamageModel,
@@ -374,7 +374,7 @@ class ExplosiveDeployableJammerTest extends ActorTest {
assert(
msg_local(2) match {
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) =>
- (target eq j_mine) && (_zone eq zone)
+ (j_mine eq target) && (_zone eq zone)
case _ => false
}
)
@@ -432,7 +432,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
val pSource = PlayerSource(player1)
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
hMineSource,
h_mine.DamageModel,
@@ -478,7 +478,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
assert(
msg_local(3) match {
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) =>
- (target eq h_mine) && (_zone eq zone)
+ (h_mine eq target) && (_zone eq zone)
case _ => false
}
)
@@ -542,7 +542,7 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
val pSource = PlayerSource(player1)
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(projectile, weapon.Definition, weapon.FireMode, pSource, 0, Vector3.Zero, Vector3.Zero),
hMineSource,
h_mine.DamageModel,
@@ -584,7 +584,7 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
assert(
msg_local(2) match {
case LocalServiceMessage.Deployables(SupportActor.ClearSpecific(List(target), _zone)) =>
- (target eq h_mine) && (_zone eq zone)
+ (h_mine eq target) && (_zone eq zone)
case _ => false
}
)
diff --git a/src/test/scala/objects/GeneratorTest.scala b/src/test/scala/objects/GeneratorTest.scala
index d334f156..4d53ae41 100644
--- a/src/test/scala/objects/GeneratorTest.scala
+++ b/src/test/scala/objects/GeneratorTest.scala
@@ -80,7 +80,7 @@ class GeneratorControlDamageTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -162,7 +162,7 @@ class GeneratorControlCriticalTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -253,7 +253,7 @@ class GeneratorControlDestroyedTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -384,7 +384,7 @@ class GeneratorControlKillsTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -507,7 +507,7 @@ class GeneratorControlNotDestroyTwice extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -597,7 +597,7 @@ class GeneratorControlNotDamageIfExplodingTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
@@ -691,7 +691,7 @@ class GeneratorControlNotRepairIfExplodingTest extends ActorTest {
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
Projectile(
projectile,
weapon.Definition,
diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala
index 56b34460..9b261bdf 100644
--- a/src/test/scala/objects/PlayerControlTest.scala
+++ b/src/test/scala/objects/PlayerControlTest.scala
@@ -413,14 +413,13 @@ class PlayerControlDamageTest extends ActorTest {
)
assert(
msg_avatar(1) match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
+ case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
- case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 2, _)) =>
- true
+ case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
@@ -437,7 +436,7 @@ class PlayerControlDamageTest extends ActorTest {
msg_avatar(3) match {
case AvatarServiceMessage(
"TestCharacter2",
- AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(10, Vector3(2, 0, 0)))
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(17, Vector3(2, 0, 0)))
) =>
true
case _ => false
diff --git a/src/test/scala/objects/ProjectileTest.scala b/src/test/scala/objects/ProjectileTest.scala
index 414e7d85..4d96ccd1 100644
--- a/src/test/scala/objects/ProjectileTest.scala
+++ b/src/test/scala/objects/ProjectileTest.scala
@@ -358,7 +358,6 @@ class ProjectileTest extends Specification {
fury_dm,
Vector3(1.2f, 3.4f, 5.6f)
)
- obj.resolution mustEqual ProjectileResolution.Hit
obj.projectile mustEqual projectile
obj.target mustEqual p2_source
obj.damage_model mustEqual fury.DamageModel
diff --git a/src/test/scala/objects/VehicleTest.scala b/src/test/scala/objects/VehicleTest.scala
index 9992e8fb..7bd0fbf7 100644
--- a/src/test/scala/objects/VehicleTest.scala
+++ b/src/test/scala/objects/VehicleTest.scala
@@ -997,7 +997,7 @@ class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest {
// val p_source = PlayerSource( Player(Avatar(0, "TestTarget", PlanetSideEmpire.NC, CharacterGender.Female, 1, CharacterVoice.Mute)) )
// val projectile = Projectile(beamer_wep.Projectile, GlobalDefinitions.beamer, beamer_wep.FireMode, p_source, GlobalDefinitions.beamer.ObjectId, Vector3.Zero, Vector3.Zero)
// val fury_dm = Vehicle(GlobalDefinitions.fury).DamageModel
-// val obj = ResolvedProjectile(ProjectileResolution.Hit, projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f))
+// val obj = ResolvedProjectile(projectile, p_source, fury_dm, Vector3(1.2f, 3.4f, 5.6f))
//
// "not charge vehicle shields if recently damaged" in {
// assert(vehicle.Shields == 0)
diff --git a/src/test/scala/objects/VitalityTest.scala b/src/test/scala/objects/VitalityTest.scala
index f5b1a74e..ee459f06 100644
--- a/src/test/scala/objects/VitalityTest.scala
+++ b/src/test/scala/objects/VitalityTest.scala
@@ -21,7 +21,7 @@ class VitalityTest extends Specification {
val pSource = PlayerSource(player)
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(player),
player.DamageModel,
@@ -69,7 +69,7 @@ class VitalityTest extends Specification {
val pSource = PlayerSource(player)
val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero)
val resprojectile = ResolvedProjectile(
- ProjectileResolution.Splash,
+ ProjectileResolution.Hit,
projectile,
SourceEntry(player),
player.DamageModel,
diff --git a/src/test/scala/objects/ZoneTest.scala b/src/test/scala/objects/ZoneTest.scala
index 520ad0c4..7ac0988e 100644
--- a/src/test/scala/objects/ZoneTest.scala
+++ b/src/test/scala/objects/ZoneTest.scala
@@ -110,7 +110,8 @@ class ZoneTest extends Specification {
zone.AddPool("pool2", (51 to 75).toList)
val obj = new TestObject()
- guid1.register(obj, "pool2").isSuccess mustEqual true
+ val registration = guid1.register(obj, "pool2")
+ registration.isSuccess mustEqual true
guid1.WhichPool(obj).contains("pool2") mustEqual true
zone.GUID(new NumberPoolHub(new LimitedNumberSource(150))) mustEqual false
@@ -214,13 +215,15 @@ class ZonePopulationTest extends ActorTest {
override def SetupNumberPools() = {}
}
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
+ val player = Player(avatar)
+ player.GUID = PlanetSideGUID(1)
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Join(avatar)
- zone.Population ! Zone.Population.Spawn(avatar, Player(avatar), null)
+ zone.Population ! Zone.Population.Spawn(avatar, player, null)
expectNoMessage(Duration.create(200, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
@@ -232,10 +235,12 @@ class ZonePopulationTest extends ActorTest {
override def SetupNumberPools() = {}
}
val avatar = Avatar(1, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
+ val player = Player(avatar)
+ player.GUID = PlanetSideGUID(1)
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
- zone.Population ! Zone.Population.Spawn(avatar, Player(avatar), null)
+ zone.Population ! Zone.Population.Spawn(avatar, player, null)
expectNoMessage(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
@@ -251,6 +256,7 @@ class ZonePopulationTest extends ActorTest {
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
+ player.GUID = PlanetSideGUID(1)
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
@@ -271,6 +277,7 @@ class ZonePopulationTest extends ActorTest {
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
+ player.GUID = PlanetSideGUID(1)
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
@@ -318,7 +325,9 @@ class ZonePopulationTest extends ActorTest {
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player1 = Player(avatar)
+ player1.GUID = PlanetSideGUID(1)
val player2 = Player(avatar)
+ player2.GUID = PlanetSideGUID(2)
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
@@ -344,6 +353,7 @@ class ZonePopulationTest extends ActorTest {
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = {} }
val avatar = Avatar(0, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
+ player.GUID = PlanetSideGUID(1)
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
@@ -364,10 +374,12 @@ class ZonePopulationTest extends ActorTest {
override def SetupNumberPools() = {}
}
val avatar = Avatar(2, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
+ val player = Player(avatar)
+ player.GUID = PlanetSideGUID(1)
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
- zone.Population ! Zone.Population.Spawn(avatar, Player(avatar), null)
+ zone.Population ! Zone.Population.Spawn(avatar, player, null)
expectNoMessage(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
@@ -388,6 +400,7 @@ class ZonePopulationTest extends ActorTest {
override def SetupNumberPools() = {}
}
val player = Player(Avatar(3, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
+ player.GUID = PlanetSideGUID(1)
player.Release
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
@@ -404,6 +417,7 @@ class ZonePopulationTest extends ActorTest {
override def SetupNumberPools() = {}
}
val player = Player(Avatar(4, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
+ player.GUID = PlanetSideGUID(1)
player.Release
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
@@ -422,10 +436,13 @@ class ZonePopulationTest extends ActorTest {
override def SetupNumberPools() = {}
}
val player1 = Player(Avatar(5, "Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
+ player1.GUID = PlanetSideGUID(1)
player1.Release
val player2 = Player(Avatar(6, "Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
+ player2.GUID = PlanetSideGUID(2)
player2.Release
val player3 = Player(Avatar(7, "Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
+ player3.GUID = PlanetSideGUID(3)
player3.Release
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)
@@ -450,6 +467,7 @@ class ZonePopulationTest extends ActorTest {
override def SetupNumberPools() = {}
}
val player = Player(Avatar(8, "Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
+ player.GUID = PlanetSideGUID(1)
//player.Release !!important
zone.actor = system.spawn(ZoneActor(zone), ZoneTest.TestName)
expectNoMessage(200 milliseconds)