adding entity to represent force dome, and wiring force dome to capitol facility; touching the force dome while it is active causes death on both server and client

This commit is contained in:
Fate-JH 2025-12-07 23:39:43 -05:00
parent 8f1badb862
commit 1d57cca1d3
8 changed files with 190 additions and 17 deletions

View file

@ -14,6 +14,7 @@ import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.{CommonMessages, ServerObject}
import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.dome.ForceDomePhysics
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.llu.CaptureFlag
@ -26,11 +27,14 @@ import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vehicles.Utility
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.etc.ForceDomeExposure
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.OutfitEventAction.{OutfitInfo, OutfitRankNames, Initial, Unk1}
import net.psforever.packet.game.OutfitEventAction.{Initial, OutfitInfo, OutfitRankNames, Unk1}
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitEvent, OutfitMemberEvent, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, OutfitRequestAction, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.RemoverActor
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -538,7 +542,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
def handleGenericCollision(pkt: GenericCollisionMsg): Unit = {
player.BailProtection = false
val GenericCollisionMsg(ctype, p, _, _, pv, _, _, _, _, _, _, _) = pkt
val GenericCollisionMsg(ctype, p, _, _, pv, t, _, _, _, _, _, _) = pkt
if (pv.z * pv.z >= (pv.x * pv.x + pv.y * pv.y) * 0.5f) {
if (ops.heightTrend) {
ops.heightHistory = ops.heightLast
@ -555,8 +559,22 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
v.BailProtection = false
case (CollisionIs.OfAircraft, Some(v: Vehicle))
if v.Definition.CanFly && v.Seats(0).occupant.contains(player) => ()
case (CollisionIs.BetweenThings, Some(field: ForceDomePhysics)) /*if field.Energized*/ =>
val target = sessionLogic
.vehicles
.findLocalVehicle
.getOrElse(player)
target.Actor ! Vitality.Damage(
DamageInteraction(
PlayerSource(player),
ForceDomeExposure(SourceEntry(field)),
player.Position
).calculate()
)
target.BailProtection = false
player.BailProtection = false
case (CollisionIs.BetweenThings, _) =>
log.warn("GenericCollision: CollisionIs.BetweenThings detected - no handling case")
log.warn(s"GenericCollision: CollisionIs.BetweenThings detected - no handling case for obj id:${t.guid}")
case _ => ()
}
}

View file

@ -16,6 +16,7 @@ import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.{PlanetSideServerObject, ServerObject}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.dome.ForceDomePhysics
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.interior.Sidedness.OutsideOf
@ -29,11 +30,11 @@ import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.FacilityTurret
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vehicles.Utility
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.collision.{CollisionReason, CollisionWithReason}
import net.psforever.objects.vital.etc.SuicideReason
import net.psforever.objects.vital.etc.{ForceDomeExposure, SuicideReason}
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
import net.psforever.packet.PlanetSideGamePacket
@ -636,6 +637,21 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
case (CollisionIs.OfAircraft, out @ Some(v: Vehicle))
if v.Definition.CanFly && v.Seats(0).occupant.contains(player) =>
(out, sessionLogic.validObject(t, decorator = "GenericCollision/Aircraft"), false, pv)
case (CollisionIs.BetweenThings, Some(field: ForceDomePhysics)) /*if field.Energized*/ =>
val target = sessionLogic
.vehicles
.findLocalVehicle
.getOrElse(player)
target.Actor ! Vitality.Damage(
DamageInteraction(
PlayerSource(player),
ForceDomeExposure(SourceEntry(field)),
player.Position
).calculate()
)
target.BailProtection = false
player.BailProtection = false
(None, None, false, Vector3.Zero)
case (CollisionIs.BetweenThings, _) =>
log.warn("GenericCollision: CollisionIs.BetweenThings detected - no handling case")
(None, None, false, Vector3.Zero)

View file

@ -8,6 +8,7 @@ import net.psforever.objects.definition.converter._
import net.psforever.objects.equipment._
import net.psforever.objects.global.{GlobalDefinitionsAmmo, GlobalDefinitionsBuilding, GlobalDefinitionsDeployable, GlobalDefinitionsExoSuit, GlobalDefinitionsImplant, GlobalDefinitionsKit, GlobalDefinitionsMiscellaneous, GlobalDefinitionsProjectile, GlobalDefinitionsTool, GlobalDefinitionsVehicle}
import net.psforever.objects.locker.LockerContainerDefinition
import net.psforever.objects.serverobject.dome.ForceDomeDefinition
import net.psforever.objects.serverobject.doors.DoorDefinition
import net.psforever.objects.serverobject.generator.GeneratorDefinition
import net.psforever.objects.serverobject.locks.IFFLockDefinition
@ -1286,6 +1287,18 @@ object GlobalDefinitions {
val zipline = new GenericTeleportationDefinition(1047)
val force_dome_generator = new ForceDomeDefinition(322)
val force_dome_amp_physics = new ForceDomeDefinition(313)
val force_dome_comm_physics = new ForceDomeDefinition(316)
val force_dome_cryo_physics = new ForceDomeDefinition(319)
val force_dome_dsp_physics = new ForceDomeDefinition(321)
val force_dome_tech_physics = new ForceDomeDefinition(323)
/*
Buildings
*/

View file

@ -0,0 +1,9 @@
// Copyright (c) 2025 PSForever
package net.psforever.objects.serverobject.dome
import net.psforever.objects.serverobject.structures.AmenityDefinition
class ForceDomeDefinition(objectId: Int)
extends AmenityDefinition(objectId) {
Name = "force_dome"
}

View file

@ -0,0 +1,40 @@
// Copyright (c) 2025 PSForever
package net.psforever.objects.serverobject.dome
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.types.Vector3
class ForceDomePhysics(private val cfddef: ForceDomeDefinition)
extends Amenity {
private var energized: Boolean = false
def Energized: Boolean = energized
def Energized_=(state: Boolean): Boolean = {
energized = state
Energized
}
def Definition: ForceDomeDefinition = cfddef
}
object ForceDomePhysics {
import akka.actor.ActorContext
/**
* Instantiate and configure a `CapitolForceDome` object.
* @param pos positon of the object in the zone's coordinate system
* @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality
* @return the `CapitolForceDome` object
*/
def Constructor(pos: Vector3)(id: Int, context: ActorContext): ForceDomePhysics = {
//import akka.actor.Props
import net.psforever.objects.GlobalDefinitions
val obj = new ForceDomePhysics(GlobalDefinitions.force_dome_generator)
obj.Position = pos
//obj.Actor = context.actorOf(Props(classOf[null], obj), s"${GlobalDefinitions.door.Name}_$id")
obj
}
}

View file

@ -0,0 +1,62 @@
// Copyright (c) 2025 PSForever
package net.psforever.objects.vital.etc
import net.psforever.objects.sourcing.{AmenitySource, SourceEntry}
import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.prop.DamageProperties
import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
/**
* A wrapper for a "damage source" in damage calculations that indicates a harmful interaction from a capitol force dome.
* @param field the target of the field in question
*/
final case class ForceDomeExposure(field: SourceEntry)
extends DamageReason {
def resolution: DamageResolution.Value = DamageResolution.Collision
def same(test: DamageReason): Boolean = test match {
case eer: ForceDomeExposure => eer.field eq field
case _ => false
}
/**
* Want to blame the capitol facility that is being protected.
*/
override def attribution: Int = field match {
case a: AmenitySource => a.installation.Definition.ObjectId
case _ => field.Definition.ObjectId
}
/**
* A direct connection to the damage information, numbers and properties.
*/
override def source: DamageProperties = ForceDomeExposure.damageProperties
/**
* The functionality that is necessary for interaction of a vital game object with the rest of the hostile game world.
*/
override def damageModel: DamageAndResistance = ForceDomeExposure.drm
/**
* The person to be blamed for this.
*/
override def adversary: Option[SourceEntry] = None
}
object ForceDomeExposure {
final val drm = new DamageResistanceModel {
DamageUsing = DamageCalculations.AgainstExoSuit
ResistUsing = NoResistanceSelection
Model = SimpleResolutions.calculate
}
final val damageProperties = new DamageProperties {
Damage0 = 99999
DamageToHealthOnly = true
DamageToVehicleOnly = true
DamageToBattleframeOnly = true
}
}

View file

@ -29,7 +29,7 @@ final case class SuicideReason()
eventually, they stop logging in.
Anyway, this has nothing to do with that.
Most playes probably just want to jump to the next base over.
Most players probably just want to jump to the next base over.
*/
def source: DamageProperties = SuicideReason.damageProperties

View file

@ -11,6 +11,7 @@ import io.circe.parser._
import net.psforever.objects.{GlobalDefinitions, LocalLockerItem, LocalProjectile}
import net.psforever.objects.definition.BasicDefinition
import net.psforever.objects.guid.selector.{NumberSelector, RandomSelector, SpecificSelector}
import net.psforever.objects.serverobject.dome.ForceDomePhysics
import net.psforever.objects.serverobject.doors.{Door, DoorDefinition, SpawnTubeDoor}
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.llu.{CaptureFlagSocket, CaptureFlagSocketDefinition}
@ -100,17 +101,9 @@ object Zones {
"PathPoints"
)(ZipLinePath.apply)
// monolith, hst, warpgate are ignored for now as the scala code isn't ready to handle them.
// BFR terminals/doors are ignored as top level elements as sanctuaries have them with no associated building. (repair_silo also has this problem, but currently is ignored in the AmenityExtrator project)
// Force domes have GUIDs but are currently classed as separate entities. The dome is controlled by sending GOAM 44 / 48 / 52 to the building GUID
private val ignoredEntities = Seq(
"monolith",
"force_dome_dsp_physics",
"force_dome_comm_physics",
"force_dome_cryo_physics",
"force_dome_tech_physics",
"force_dome_amp_physics"
)
private val ignoredEntities = Seq("monolith")
private val towerTypes = Seq("tower_a", "tower_b", "tower_c")
private val facilityTypes = Seq("amp_station", "cryo_facility", "comm_station", "comm_station_dsp", "tech_plant")
@ -127,6 +120,13 @@ object Zones {
"vt_spawn",
"vt_vehicle"
)
private val forceDomeTypes = Seq(
"force_dome_dsp_physics",
"force_dome_comm_physics",
"force_dome_cryo_physics",
"force_dome_tech_physics",
"force_dome_amp_physics"
)
private val cavernBuildingTypes = Seq(
"ceiling_bldg_a",
"ceiling_bldg_b",
@ -380,11 +380,27 @@ object Zones {
createObjects(
zoneMap,
zoneObjects.filterNot { _.objectType.startsWith("bfr_") },
zoneObjects.filterNot { obj => obj.objectType.startsWith("bfr_") || forceDomeTypes.contains(obj.objectType) },
ownerGuid = 0,
None,
turretWeaponGuid
)
//force dome physics object are not owned
//for our benefit, we can attach them as amenities to the zone's capitol facility
zoneObjects
.find { obj => forceDomeTypes.contains(obj.objectType) }
.foreach { forceDome =>
structures
.find { structure => Building.Capitols.contains(structure.objectName) }
.foreach { capitol =>
zoneMap
.addLocalObject(
forceDome.guid,
ForceDomePhysics.Constructor(forceDome.position),
owningBuildingGuid = capitol.guid
)
}
}
lattice.asObject.get(mapid).foreach { obj =>
obj.asArray.get.foreach { entry =>
@ -710,7 +726,6 @@ object Zones {
case _ => ()
}
}
}