PSF-BotServer/src/main/scala/net/psforever/objects/TurretDeployable.scala
Fate-JH 93a544c07c
Collisions (#932)
* pattern for applying damage to player avatar and player-controlled vehicle collisions

* pattern for applying damage to targets due to collisions, falling damage and crashing damage individually; fields to support these calculations are provided

* modifiers to translate 'small step velocity' to real game velocity, as reported by the HUD; corrections for velocity; corrections for velocity in other packets

* fall damage calculations moved to function

* basic two-body collisions between GUID-identified game entities and a ward against too many collisions in a short amount of time

* bailing mechanics

* vssm for non-driven vehicles

* comment about vehicle state message field

* comments and minor refactoring for current collision damage calc; tank_traps modifier; potential fix for blockmap indexing issue

* fixed cargo/carrier vehicle ops

* corrections to initialization of ce construction items; adjustments to handling of modifiers for collision damage

* modifier change, protection against flight speed and spectator crashes; submerged status is once again known only to the actor

* appeasing the automated tests

* hopefully paced collisions better; re-did how Infantry collisions are calculated, incorporating mass and exo-suit data; kill feed reporting should be better

* adjusted damage values again, focusing on the lesser of or middling results; collision killfeed attribution attempt

* kicking offers bail protection; lowered the artificial modifier for one kind of collision damage calculation

* correction to the local reference map functions

* fixed tests; attempt to zero fall damage distance based on velocity; attempt to block mine damage when spectating
2021-10-05 09:59:49 -04:00

139 lines
5.1 KiB
Scala

// Copyright (c) 2017 PSForever
package net.psforever.objects
import akka.actor.{Actor, ActorContext, Props}
import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem}
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.definition.converter.SmallTurretConverter
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target
import net.psforever.objects.serverobject.damage.DamageableWeaponTurret
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.repair.RepairableWeaponTurret
import net.psforever.objects.serverobject.turret.{TurretDefinition, WeaponTurret}
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration.FiniteDuration
class TurretDeployable(tdef: TurretDeployableDefinition)
extends Deployable(tdef)
with WeaponTurret
with JammableUnit
with Hackable {
WeaponTurret.LoadDefinition(this)
override def Definition = tdef
}
class TurretDeployableDefinition(private val objectId: Int)
extends DeployableDefinition(objectId)
with TurretDefinition {
Name = "turret_deployable"
Packet = new SmallTurretConverter
DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = StandardVehicleResistance
Model = SimpleResolutions.calculate
//override to clarify inheritance conflict
override def MaxHealth: Int = super[DeployableDefinition].MaxHealth
//override to clarify inheritance conflict
override def MaxHealth_=(max: Int): Int = super[DeployableDefinition].MaxHealth_=(max)
override def Initialize(obj: Deployable, context: ActorContext) = {
obj.Actor = context.actorOf(Props(classOf[TurretControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
}
object TurretDeployableDefinition {
def apply(dtype: DeployedItem.Value): TurretDeployableDefinition = {
new TurretDeployableDefinition(dtype.id)
}
}
/** control actors */
class TurretControl(turret: TurretDeployable)
extends Actor
with DeployableBehavior
with FactionAffinityBehavior.Check
with JammableMountedWeapons //note: jammable status is reported as vehicle events, not local events
with MountableBehavior
with DamageableWeaponTurret
with RepairableWeaponTurret {
def DeployableObject = turret
def MountableObject = turret
def JammableObject = turret
def FactionObject = turret
def DamageableObject = turret
def RepairableObject = turret
override def postStop(): Unit = {
super.postStop()
deployableBehaviorPostStop()
damageableWeaponTurretPostStop()
}
def receive: Receive =
deployableBehavior
.orElse(checkBehavior)
.orElse(jammableBehavior)
.orElse(mountBehavior)
.orElse(dismountBehavior)
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
.orElse {
case _ => ;
}
override protected def mountTest(
obj: PlanetSideServerObject with Mountable,
seatNumber: Int,
player: Player): Boolean = {
(!turret.Definition.FactionLocked || player.Faction == obj.Faction) && !obj.Destroyed
}
override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
super.DestructionAwareness(target, cause)
CancelJammeredSound(target)
CancelJammeredStatus(target)
Deployables.AnnounceDestroyDeployable(turret, None)
}
override def deconstructDeployable(time: Option[FiniteDuration]) : Unit = {
val zone = turret.Zone
val seats = turret.Seats.values
//either we have no seats or no one gets to sit
val retime = if (seats.count(_.isOccupied) > 0) {
//unlike with vehicles, it's possible to request deconstruction of one's own field turret while seated in it
val wasKickedByDriver = false
seats.foreach { seat =>
seat.occupant match {
case Some(tplayer) =>
seat.unmount(tplayer)
tplayer.VehicleSeated = None
zone.VehicleEvents ! VehicleServiceMessage(
zone.id,
VehicleAction.KickPassenger(tplayer.GUID, 4, wasKickedByDriver, turret.GUID)
)
case None => ;
}
}
Some(time.getOrElse(Deployable.cleanup) + Deployable.cleanup)
} else {
time
}
super.deconstructDeployable(retime)
}
override def unregisterDeployable(obj: Deployable): Unit = {
val zone = obj.Zone
TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, turret))
}
}