mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
initial AIDamage packet and tests; wrote handling code for the AIDamage packet that transforms it into actionable projectile damage
This commit is contained in:
parent
9952803883
commit
41462ce540
|
|
@ -518,6 +518,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
case packet: LashMessage =>
|
||||
sessionFuncs.shooting.handleLashHit(packet)
|
||||
|
||||
case packet: AIDamage =>
|
||||
sessionFuncs.shooting.handleAIDamage(packet)
|
||||
|
||||
case packet: AvatarFirstTimeEventMessage =>
|
||||
sessionFuncs.handleAvatarFirstTimeEvent(packet)
|
||||
|
||||
|
|
|
|||
|
|
@ -2202,7 +2202,7 @@ class SessionData(
|
|||
|
||||
/**
|
||||
* Calculate the amount of damage to be dealt to an active `target`
|
||||
* using the information reconstructed from a `Resolvedprojectile`
|
||||
* using the information reconstructed from a `ResolvedProjectile`
|
||||
* and affect the `target` in a synchronized manner.
|
||||
* The active `target` and the target of the `DamageResult` do not have be the same.
|
||||
* While the "tell" for being able to sustain damage is an entity of type `Vitality`,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package net.psforever.actors.session.support
|
|||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.zones.Zoning
|
||||
import net.psforever.objects.serverobject.turret.AutomatedTurret
|
||||
import net.psforever.objects.zones.exp.ToDatabase
|
||||
|
||||
import scala.collection.mutable
|
||||
|
|
@ -430,6 +431,47 @@ private[support] class WeaponAndProjectileOperations(
|
|||
}
|
||||
}
|
||||
|
||||
def handleAIDamage(pkt: AIDamage): Unit = {
|
||||
val AIDamage(_, attackerGuid, projectileTypeId, _, _) = pkt
|
||||
sessionData.validObject(attackerGuid, decorator = "AIDamage/Entity")
|
||||
.collect {
|
||||
case turret: AutomatedTurret with OwnableByPlayer =>
|
||||
val owner = sessionData.validObject(turret.OwnerGuid, decorator = "AIDamage/Owner")
|
||||
.collect { case obj: PlanetSideGameObject with FactionAffinity => SourceEntry(obj) }
|
||||
.getOrElse { SourceEntry.None }
|
||||
turret.Weapons
|
||||
.values
|
||||
.flatMap { _.Equipment }
|
||||
.collect { case weapon: Tool => (turret, weapon, owner, weapon.Projectile) }
|
||||
.find { case (_, _, _, p) => p.ObjectId == projectileTypeId }
|
||||
|
||||
}
|
||||
.collect {
|
||||
case Some((obj, tool, owner, projectileInfo)) =>
|
||||
val target = sessionData.validObject(player.VehicleSeated, decorator = "AIDamage/Entity") match {
|
||||
case Some(tobj: PlanetSideGameObject with FactionAffinity with Vitality) => tobj
|
||||
case _ => player
|
||||
}
|
||||
val angle = Vector3.Unit(target.Position - obj.Position)
|
||||
val proj = new Projectile(
|
||||
projectileInfo,
|
||||
tool.Definition,
|
||||
tool.FireMode,
|
||||
None,
|
||||
owner,
|
||||
obj.Definition.ObjectId,
|
||||
obj.Position + Vector3.z(value = 1f),
|
||||
angle,
|
||||
Some(angle * projectileInfo.FinalVelocity)
|
||||
)
|
||||
val hitPos = target.Position + Vector3.z(value = 1f)
|
||||
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* support code */
|
||||
|
||||
def HandleWeaponFireOperations(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import net.psforever.objects.{Default, Player}
|
|||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.sourcing.{SourceEntry, SourceUniqueness}
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.packet.game.{ChangeFireStateMessage_Start, ObjectDetectedMessage}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||
|
||||
|
|
@ -100,14 +101,8 @@ trait AutomatedTurretBehavior {
|
|||
private def newDetectedTarget(target: Target): Unit = {
|
||||
target match {
|
||||
case target: Player =>
|
||||
target.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, List(target.GUID)))
|
||||
)
|
||||
target.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
AvatarAction.SendResponse(PlanetSideGUID(0), ChangeFireStateMessage_Start(AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID))
|
||||
)
|
||||
startTrackingTargets(target.Zone, target.Name, List(target.GUID))
|
||||
startShooting(target.Zone, target.Name, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
|
@ -115,14 +110,40 @@ trait AutomatedTurretBehavior {
|
|||
private def noLongerDetectedTarget(target: Target): Unit = {
|
||||
target match {
|
||||
case target: Player =>
|
||||
target.Zone.AvatarEvents ! AvatarServiceMessage(
|
||||
target.Name,
|
||||
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, List(PlanetSideGUID(0))))
|
||||
)
|
||||
stopTrackingTargets(target.Zone, target.Name)
|
||||
stopShooting(target.Zone, target.Name, AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def startTrackingTargets(zone: Zone, channel: String, list: List[PlanetSideGUID]): Unit = {
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, list))
|
||||
)
|
||||
}
|
||||
|
||||
def stopTrackingTargets(zone: Zone, channel: String): Unit = {
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.SendResponse(PlanetSideGUID(0), ObjectDetectedMessage(AutomatedTurretObject.GUID, AutomatedTurretObject.GUID, 0, List(PlanetSideGUID(0))))
|
||||
)
|
||||
}
|
||||
|
||||
def startShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.SendResponse(PlanetSideGUID(0), ChangeFireStateMessage_Start(weaponGuid))
|
||||
)
|
||||
}
|
||||
|
||||
def stopShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = {
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
channel,
|
||||
AvatarAction.SendResponse(PlanetSideGUID(0), ChangeFireStateMessage_Stop(weaponGuid))
|
||||
)
|
||||
}
|
||||
|
||||
private def testDistanceCheckQualifications(beforeSize: Int): Unit = {
|
||||
if (beforeSize > 0 && AutomatedTurretObject.Targets.isEmpty) {
|
||||
targetDistanceCheck.cancel()
|
||||
|
|
|
|||
|
|
@ -411,7 +411,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x59 => noDecoder(UnknownMessage89)
|
||||
case 0x5a => game.DelayedPathMountMsg.decode
|
||||
case 0x5b => game.OrbitalShuttleTimeMsg.decode
|
||||
case 0x5c => noDecoder(AIDamage)
|
||||
case 0x5c => game.AIDamage.decode
|
||||
case 0x5d => game.DeployObjectMessage.decode
|
||||
case 0x5e => game.FavoritesRequest.decode
|
||||
case 0x5f => noDecoder(FavoritesResponse)
|
||||
|
|
|
|||
32
src/main/scala/net/psforever/packet/game/AIDamage.scala
Normal file
32
src/main/scala/net/psforever/packet/game/AIDamage.scala
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2023 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* ...
|
||||
*/
|
||||
final case class AIDamage(
|
||||
target_guid: PlanetSideGUID,
|
||||
attacker_guid: PlanetSideGUID,
|
||||
projectile_type: Long,
|
||||
unk1: Long,
|
||||
unk2: Long
|
||||
) extends PlanetSideGamePacket {
|
||||
type Packet = ActionResultMessage
|
||||
def opcode = GamePacketOpcode.AIDamage
|
||||
def encode = AIDamage.encode(this)
|
||||
}
|
||||
|
||||
object AIDamage extends Marshallable[AIDamage] {
|
||||
implicit val codec: Codec[AIDamage] = (
|
||||
("target_guid" | PlanetSideGUID.codec) ::
|
||||
("attacker_guid" | PlanetSideGUID.codec) ::
|
||||
("projectile_type" | ulongL(bits = 32)) ::
|
||||
("unk1" | ulongL(bits = 32)) ::
|
||||
("unk2" | ulongL(bits = 32))
|
||||
).as[AIDamage]
|
||||
}
|
||||
32
src/test/scala/game/AIDamageTest.scala
Normal file
32
src/test/scala/game/AIDamageTest.scala
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2023 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import scodec.bits._
|
||||
|
||||
class AIDamageTest extends Specification {
|
||||
val string1 = hex"5c de10 89e8 38030000 00000000 04020000"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.decodePacket(string1).require match {
|
||||
case AIDamage(target_guid, attacker_guid, projectile_type, unk1, unk2) =>
|
||||
target_guid mustEqual PlanetSideGUID(4318)
|
||||
attacker_guid mustEqual PlanetSideGUID(59529)
|
||||
projectile_type mustEqual 824L
|
||||
unk1 mustEqual 0L
|
||||
unk2 mustEqual 516L
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = AIDamage(PlanetSideGUID(4318), PlanetSideGUID(59529), 824L, 0L, 516L)
|
||||
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string1
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue