Merge branch 'master' into forseral-water

This commit is contained in:
Fate-JH 2024-03-29 00:36:49 -04:00 committed by GitHub
commit b76ab4cae6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
61 changed files with 11344 additions and 9634 deletions

View file

@ -9,6 +9,7 @@ Code Contributors
* aphedox
* L-11
* xTriad (initial database schema)
* Jakob
Packet Capturing 2015-2016 (in order of most packets captured)
=================

View file

@ -542,9 +542,12 @@ network {
}
development {
# List of GM commands available to everyone
# List of GM commands made available to everyone
# Values are `ChatMessageType` members, for example: [CMT_ADDBATTLEEXPERIENCE, CMT_CAPTUREBASE]
unprivileged-gm-commands = []
# List of GM bang commands made available to everyone
# Since the commands come in as plain chat preceded by a bang (!) character, values are the names of the commands
unprivileged-gm-bang-commands = []
net-sim {
# Enable artificial packet unreliability. Used for development testing.

View file

@ -9,7 +9,7 @@ import java.util.concurrent.atomic.AtomicInteger
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill, Life, ScoreCard, SupportActivity}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.sourcing.VehicleSource
import net.psforever.objects.sourcing.{TurretSource, VehicleSource}
import net.psforever.objects.vital.{InGameHistory, ReconstructionActivity}
import net.psforever.objects.vehicles.MountedWeapons
import net.psforever.types.{ChatMessageType, StatisticalCategory, StatisticalElement}
@ -3188,10 +3188,16 @@ class AvatarActor(
player.HistoryAndContributions()
}
val target = killStat.info.targetAfter.asInstanceOf[PlayerSource]
val targetMounted = target.seatedIn.map { case (v: VehicleSource, seat) =>
val definition = v.Definition
definition.ObjectId * 10 + Vehicles.SeatPermissionGroup(definition, seat).map { _.id }.getOrElse(0)
}.getOrElse(0)
val targetMounted = target.seatedIn
.collect {
case (v: VehicleSource, seat) =>
val definition = v.Definition
definition.ObjectId * 10 + Vehicles.SeatPermissionGroup(definition, seat).map { _.id }.getOrElse(0)
case (t: TurretSource, seat) =>
val definition = t.Definition
definition.ObjectId * 10 + seat
}
.getOrElse(0)
zones.exp.ToDatabase.reportKillBy(
avatar.id.toLong,
target.CharId,

File diff suppressed because it is too large Load diff

View file

@ -641,6 +641,7 @@ class SessionData(
val dObj: Deployable = Deployables.Make(ammoType)()
dObj.Position = pos
dObj.Orientation = orient
dObj.WhichSide = player.WhichSide
dObj.Faction = player.Faction
dObj.AssignOwnership(player)
val tasking: TaskBundle = dObj match {
@ -1449,6 +1450,7 @@ class SessionData(
}) match {
case Some((vehicle: Vehicle, Some(util: Utility.InternalTelepad))) =>
zoning.CancelZoningProcessWithDescriptiveReason("cancel")
player.WhichSide = vehicle.WhichSide
useRouterTelepadSystem(
router = vehicle,
internalTelepad = util,
@ -1474,6 +1476,7 @@ class SessionData(
continent.GUID(obj.Telepad) match {
case Some(pad: TelepadDeployable) =>
zoning.CancelZoningProcessWithDescriptiveReason("cancel")
player.WhichSide = pad.WhichSide
useRouterTelepadSystem(
router = obj.Owner.asInstanceOf[Vehicle],
internalTelepad = obj,

View file

@ -3,6 +3,8 @@ package net.psforever.actors.session.support
import akka.actor.{ActorContext, typed}
import net.psforever.objects.definition.ProjectileDefinition
import net.psforever.objects.serverobject.doors.InteriorDoorPassage
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior}
import net.psforever.objects.zones.Zoning
import net.psforever.objects.serverobject.turret.VanuSentry
@ -303,11 +305,11 @@ private[support] class WeaponAndProjectileOperations(
//find target(s)
(hit_info match {
case Some(hitInfo) =>
val hitPos = hitInfo.hit_pos
val hitPos = hitInfo.hit_pos
sessionData.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match {
case _ if projectile.profile == GlobalDefinitions.flail_projectile =>
val radius = projectile.profile.DamageRadius * projectile.profile.DamageRadius
val targets = Zone.findAllTargets(hitPos)(continent, player, projectile.profile)
val targets = Zone.findAllTargets(continent, player, hitPos, projectile.profile)
.filter { target =>
Vector3.DistanceSquared(target.Position, hitPos) <= radius
}
@ -361,7 +363,6 @@ private[support] class WeaponAndProjectileOperations(
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) =>
@ -372,7 +373,7 @@ private[support] class WeaponAndProjectileOperations(
//direct_victim_uid
sessionData.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match {
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
ResolveProjectileInteraction(projectile, resolution1, target, target.Position).collect { resprojectile =>
addShotsLanded(resprojectile.cause.attribution, shots = 1)
sessionData.handleDealingDamage(target, resprojectile)
@ -563,6 +564,7 @@ private[support] class WeaponAndProjectileOperations(
ProjectileQuality.Normal
}
val qualityprojectile = projectile.quality(initialQuality)
qualityprojectile.WhichSide = player.WhichSide
projectiles(projectileIndex) = Some(qualityprojectile)
if (projectile_info.ExistsOnRemoteClients) {
log.trace(
@ -1048,19 +1050,22 @@ private[support] class WeaponAndProjectileOperations(
GlobalDefinitions.getDamageProxy(projectile, hitPos) match {
case Nil =>
Nil
case list if list.isEmpty =>
Nil
case list =>
HandleDamageProxySetupLittleBuddy(list, hitPos)
UpdateProjectileSidednessAfterHit(projectile, hitPos)
val projectileSide = projectile.WhichSide
list.flatMap { proxy =>
if (proxy.profile.ExistsOnRemoteClients) {
proxy.Position = hitPos
proxy.WhichSide = projectileSide
continent.Projectile ! ZoneProjectile.Add(player.GUID, proxy)
Nil
} else if (proxy.tool_def == GlobalDefinitions.maelstrom) {
//server-side maelstrom grenade target selection
val radius = proxy.profile.LashRadius * proxy.profile.LashRadius
val targets = continent.blockMap
.sector(hitPos, proxy.profile.LashRadius)
.livePlayerList
val targets = Zone.findAllTargets(continent, hitPos, proxy.profile.LashRadius, { _.livePlayerList })
.filter { target =>
Vector3.DistanceSquared(target.Position, hitPos) <= radius
}
@ -1498,6 +1503,88 @@ private[support] class WeaponAndProjectileOperations(
}
}
private def UpdateProjectileSidednessAfterHit(projectile: Projectile, hitPosition: Vector3): Unit = {
val origin = projectile.Position
val distance = Vector3.Magnitude(hitPosition - origin)
continent.blockMap
.sector(hitPosition, distance)
.environmentList
.collect { case o: InteriorDoorPassage =>
val door = o.door
val intersectTest = quickLineSphereIntersectionPoints(
origin,
hitPosition,
door.Position,
door.Definition.UseRadius + 0.1f
)
(door, intersectTest)
}
.collect { case (door, intersectionTest) if intersectionTest.nonEmpty =>
(door, Vector3.Magnitude(hitPosition - door.Position), intersectionTest)
}
.minByOption { case (_, dist, _) => dist }
.foreach { case (door, _, intersects) =>
val strictly = if (Vector3.DotProduct(Vector3.Unit(hitPosition - door.Position), door.Outwards) > 0f) {
Sidedness.OutsideOf
} else {
Sidedness.InsideOf
}
projectile.WhichSide = if (intersects.size == 1) {
Sidedness.InBetweenSides(door, strictly)
} else {
strictly
}
}
}
/**
* Does a line segment line intersect with a sphere?<br>
* This most likely belongs in `Geometry` or `GeometryForm` or somehow in association with the `\objects\geometry\` package.
* @param start first point of the line segment
* @param end second point of the line segment
* @param center center of the sphere
* @param radius radius of the sphere
* @return list of all points of intersection, if any
* @see `Vector3.DistanceSquared`
* @see `Vector3.MagnitudeSquared`
*/
private def quickLineSphereIntersectionPoints(
start: Vector3,
end: Vector3,
center: Vector3,
radius: Float
): Iterable[Vector3] = {
/*
Algorithm adapted from code found on https://paulbourke.net/geometry/circlesphere/index.html#linesphere,
because I kept messing up proper substitution of the line formula and the circle formula into the quadratic equation.
*/
val Vector3(cx, cy, cz) = center
val Vector3(sx, sy, sz) = start
val vector = end - start
//speed our way through a quadratic equation
val (a, b) = {
val Vector3(dx, dy, dz) = vector
(
dx * dx + dy * dy + dz * dz,
2f * (dx * (sx - cx) + dy * (sy - cy) + dz * (sz - cz))
)
}
val c = Vector3.MagnitudeSquared(center) + Vector3.MagnitudeSquared(start) - 2f * (cx * sx + cy * sy + cz * sz) - radius * radius
val result = b * b - 4 * a * c
if (result < 0f) {
//negative, no intersection
Seq()
} else if (result < 0.00001f) {
//zero-ish, one intersection point
Seq(start - vector * (b / (2f * a)))
} else {
//positive, two intersection points
val sqrt = math.sqrt(result).toFloat
val endStart = vector / (2f * a)
Seq(start + endStart * (sqrt - b), start + endStart * (b + sqrt) * -1f)
}.filter(p => Vector3.DistanceSquared(start, p) <= a)
}
override protected[session] def stop(): Unit = {
if (player != null && player.HasGUID) {
(prefire ++ shooting).foreach { guid =>

View file

@ -10,6 +10,7 @@ import net.psforever.login.WorldSession
import net.psforever.objects.avatar.BattleRank
import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics}
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.serverobject.mount.Seat
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret
@ -2630,15 +2631,19 @@ class ZoningOperations(
} else {
Zones.zones.find { _.id.equals(zoneId) }.orElse(Some(Zone.Nowhere)).get.Number
}
val toSide = physSpawnPoint.map(_.Owner) match {
case Some(_: WarpGate) => Sidedness.OutsideOf
case Some(_: Building) => Sidedness.InsideOf
case Some(v: Vehicle) => v.WhichSide //though usually OutsideOf
case _ => Sidedness.StrictlyBetweenSides //todo needs better determination
}
val toSpawnPoint = physSpawnPoint.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }
respawnTimer = context.system.scheduler.scheduleOnce(respawnTime) {
if (player.isBackpack) { // if the player is dead, he is handled as dead infantry, even if he died in a vehicle
// new player is spawning
val newPlayer = RespawnClone(player)
newPlayer.Position = pos
newPlayer.Orientation = ori
newPlayer.LogActivity(SpawningActivity(PlayerSource(newPlayer), toZoneNumber, toSpawnPoint))
LoadZoneAsPlayer(newPlayer, zoneId)
LoadZoneAsPlayUsing(newPlayer, pos, ori, toSide, zoneId)
} else {
avatarActor ! AvatarActor.DeactivateActiveImplants()
val betterSpawnPoint = physSpawnPoint.collect { case o: PlanetSideGameObject with FactionAffinity with InGameHistory => o }
@ -2646,6 +2651,7 @@ class ZoningOperations(
case Some(vehicle: Vehicle) => // driver or passenger in vehicle using a warp gate, or a droppod
InGameHistory.SpawnReconstructionActivity(vehicle, toZoneNumber, betterSpawnPoint)
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
vehicle.WhichSide = toSide
LoadZoneInVehicle(vehicle, pos, ori, zoneId)
case _ if player.HasGUID => // player is deconstructing self or instant action
@ -2655,21 +2661,38 @@ class ZoningOperations(
continent.id,
AvatarAction.ObjectDelete(player_guid, player_guid, 4)
)
player.Position = pos
player.Orientation = ori
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
LoadZoneAsPlayer(player, zoneId)
LoadZoneAsPlayUsing(player, pos, ori, toSide, zoneId)
case _ => //player is logging in
player.Position = pos
player.Orientation = ori
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
LoadZoneAsPlayer(player, zoneId)
LoadZoneAsPlayUsing(player, pos, ori, toSide, zoneId)
}
}
}
}
/**
* na
* @param target player being spawned
* @param position where player is being placed in the game wqrld
* @param orientation in what direction the player is facing in the game world
* @param onThisSide description of the containing environment
* @param goingToZone common designation for the zone
*/
private def LoadZoneAsPlayUsing(
target: Player,
position: Vector3,
orientation: Vector3,
onThisSide: Sidedness,
goingToZone: String
): Unit = {
target.Position = position
target.Orientation = orientation
target.WhichSide = onThisSide
LoadZoneAsPlayer(target, goingToZone)
}
/**
* The user is either already in the current zone and merely transporting from one location to another,
* also called "dying", or occasionally "deconstructing,"

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.objects.avatar.interaction.{WithGantry, WithLava, WithWater}
import net.psforever.objects.avatar.interaction.{WithEntrance, WithGantry, WithLava, WithWater}
import net.psforever.objects.avatar.{Avatar, LoadoutManager, SpecialCarry}
import net.psforever.objects.ballistics.InteractWithRadiationClouds
import net.psforever.objects.ce.{Deployable, InteractWithMines, InteractWithTurrets}
@ -12,6 +12,7 @@ import net.psforever.objects.serverobject.{PlanetSideServerObject, environment}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.aura.AuraContainer
import net.psforever.objects.serverobject.environment.interaction.common.{WithDeath, WithMovementTrigger}
import net.psforever.objects.serverobject.interior.InteriorAwareFromInteraction
import net.psforever.objects.serverobject.mount.MountableEntity
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.objects.vital.{HealFromEquipment, InGameActivity, RepairFromEquipment, Vitality}
@ -35,9 +36,11 @@ class Player(var avatar: Avatar)
with Container
with JammableUnit
with ZoneAware
with InteriorAwareFromInteraction
with AuraContainer
with MountableEntity {
interaction(environment.interaction.InteractWithEnvironment(Seq(
new WithEntrance(),
new WithWater(avatar.name),
new WithLava(),
new WithDeath(),

View file

@ -162,18 +162,10 @@ object SpawnPoint {
}
trait SpawnPointDefinition {
private var radius: Float = 0f //m
private var delay: Long = 0 //s
private var noWarp: Option[mutable.Set[VehicleDefinition]] = None
private var spawningFunc: (SpawnPoint, PlanetSideGameObject) => (Vector3, Vector3) = SpawnPoint.Default
def UseRadius: Float = radius
def UseRadius_=(rad: Float): Float = {
radius = rad
UseRadius
}
def Delay: Long = delay
def Delay_=(toDelay: Long): Long = {

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.objects.avatar.interaction.WithEntrance
import net.psforever.objects.ce.{InteractWithMines, InteractWithTurrets}
import net.psforever.objects.definition.{ToolDefinition, VehicleDefinition}
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
@ -12,6 +13,7 @@ import net.psforever.objects.serverobject.aura.AuraContainer
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.environment.interaction.common.{WithDeath, WithMovementTrigger}
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.interior.{InteriorAwareFromInteraction, Sidedness}
import net.psforever.objects.serverobject.structures.AmenityOwner
import net.psforever.objects.vehicles._
import net.psforever.objects.vehicles.interaction.{WithLava, WithWater}
@ -90,8 +92,10 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
with CommonNtuContainer
with Container
with AuraContainer
with MountableEntity {
with MountableEntity
with InteriorAwareFromInteraction {
interaction(environment.interaction.InteractWithEnvironment(Seq(
new WithEntrance(),
new WithWater(),
new WithLava(),
new WithDeath(),
@ -513,6 +517,12 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
!Definition.CanFly && super.BailProtection_=(protect)
}
override def WhichSide_=(thisSide: Sidedness): Sidedness = {
val toSide = super.WhichSide_=(thisSide)
(Seats.values ++ CargoHolds.values).flatMap(_.occupants).foreach(_.WhichSide = toSide)
toSide
}
/**
* This is the definition entry that is used to store and unload pertinent information about the `Vehicle`.
* @return the vehicle's definition entry

View file

@ -0,0 +1,127 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.avatar.interaction
import net.psforever.objects.serverobject.doors.{Door, InteriorDoorPassage}
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, PieceOfEnvironment, interaction}
import net.psforever.objects.serverobject.environment.interaction.{InteractionWith, RespondsToZoneEnvironment}
import net.psforever.objects.serverobject.interior.{Sidedness, TraditionalInteriorAware}
import net.psforever.objects.zones.InteractsWithZone
import net.psforever.types.Vector3
import scala.annotation.unused
import scala.concurrent.duration._
class WithEntrance()
extends InteractionWith
with TraditionalInteriorAware {
val attribute: EnvironmentTrait = EnvironmentAttribute.InteriorField
private var stopTest: Boolean = false
def doInteractingWith(
obj: InteractsWithZone,
body: PieceOfEnvironment,
data: Option[Any]
): Unit = {
if (stopTest && data.contains("bellybutton")) {
stopTest = false
} else {
doorInteriorCheck(obj, body.asInstanceOf[InteriorDoorPassage].door)
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = 250.milliseconds, obj.Actor, interaction.InteractingWithEnvironment(body, Some("bellybutton")))
}
}
override def stopInteractingWith(
obj: InteractsWithZone,
body: PieceOfEnvironment,
data: Option[Any]
): Unit = {
straightforwardInteriorCheck(obj, body.asInstanceOf[InteriorDoorPassage].door)
stopTest = true
}
private def doorInteriorCheck(
obj: InteractsWithZone,
door: Door
): Sidedness = {
val strictly = interiorCheck(obj, door)
val value = if (door.isOpen) {
Sidedness.InBetweenSides(door, strictly)
} else {
strictly
}
WhichSide_=(value)
value
}
private def straightforwardInteriorCheck(
obj: InteractsWithZone,
door: Door
): Sidedness = {
WhichSide_=(interiorCheck(obj, door))
}
private def interiorCheck(
obj: InteractsWithZone,
door: Door
): Sidedness = {
//debugInteriorCheck(obj, door)
strictInteriorCheck(obj, door)
}
@unused
private def strictInteriorCheck(
obj: InteractsWithZone,
door: Door
): Sidedness = {
if (door.Outwards == Vector3.Zero) {
WhichSide
} else if (Vector3.DotProduct(Vector3.Unit(obj.Position - door.Position), door.Outwards) > 0f) {
Sidedness.OutsideOf
} else {
Sidedness.InsideOf
}
}
@unused
private def debugInteriorCheck(
obj: InteractsWithZone,
door: Door
): Sidedness = {
import net.psforever.objects.{Player, Vehicle}
import net.psforever.packet.game.ChatMsg
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{ChatMessageType, PlanetSideGUID}
val channel = obj match {
case p: Player => p.Name
case v: Vehicle => v.Actor.toString()
case _ => ""
}
if (door.Outwards == Vector3.Zero) {
obj.Zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "Door not configured."))
)
WhichSide
} else {
val result = Vector3.DotProduct(Vector3.Unit(obj.Position - door.Position), door.Outwards) > 0f
if (result && WhichSide != Sidedness.OutsideOf) {
//outside
obj.Zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "You are now outside"))
)
Sidedness.OutsideOf
} else if (!result && WhichSide != Sidedness.InsideOf) {
//inside
obj.Zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "You are now inside"))
)
Sidedness.InsideOf
} else {
WhichSide
}
}
}
}

View file

@ -29,7 +29,7 @@ class InteractWithRadiationClouds(
*/
private var skipTargets: List[PlanetSideGUID] = List()
def Type = RadiationInteraction
def Type: ZoneInteractionType = RadiationInteraction
/**
* Wander into a radiation cloud and suffer the consequences.
@ -40,12 +40,16 @@ class InteractWithRadiationClouds(
target match {
case t: Vitality =>
val position = target.Position
val targetList = List(target)
//collect all projectiles in sector/range
val projectiles = sector
.projectileList
.filter { cloud =>
val radius = cloud.Definition.DamageRadius
cloud.Definition.radiation_cloud && Zone.distanceCheck(target, cloud, radius * radius)
val definition = cloud.Definition
val radius = definition.DamageRadius
definition.radiation_cloud &&
Zone.allOnSameSide(cloud, definition, targetList).nonEmpty &&
Zone.distanceCheck(target, cloud, radius * radius)
}
.distinct
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
import net.psforever.objects.serverobject.interior.TraditionalInteriorAware
import net.psforever.objects.sourcing.SourceEntry
import java.util.concurrent.atomic.AtomicLong
@ -53,7 +54,8 @@ final case class Projectile(
id: Long = Projectile.idGenerator.getAndIncrement(),
fire_time: Long = System.currentTimeMillis()
) extends PlanetSideGameObject
with BlockMapEntity {
with BlockMapEntity
with TraditionalInteriorAware {
Position = shot_origin
Orientation = shot_angle
Velocity = shot_velocity.getOrElse {
@ -73,7 +75,8 @@ final case class Projectile(
* 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.
* It is important to note that the new projectile shares the (otherwise) exclusive id of the original
* and that it is not added to a block map structure.
* @param value the new quality
* @return a new `Projectile` entity
*/
@ -94,6 +97,7 @@ final case class Projectile(
)
if(isMiss) projectile.Miss()
else if(isResolved) projectile.Resolve()
projectile.WhichSide = this.WhichSide
projectile
}

View file

@ -5,6 +5,7 @@ import net.psforever.objects._
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.interior.TraditionalInteriorAware
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.resolution.DamageResistanceModel
import net.psforever.objects.zones.ZoneAware
@ -18,7 +19,8 @@ trait BaseDeployable
with BlockMapEntity
with Vitality
with OwnableByPlayer
with ZoneAware {
with ZoneAware
with TraditionalInteriorAware {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var shields: Int = 0

View file

@ -20,13 +20,23 @@ import net.psforever.types.OxygenState
* So long as it is an `ObjectCreatePacket`, those methods can be called correctly for a game object of the desired type.
* @param objectId the object's identifier number
*/
abstract class ObjectDefinition(private val objectId: Int) extends BasicDefinition {
abstract class ObjectDefinition(private val objectId: Int)
extends BasicDefinition {
var registerAs: String = "generic"
/** a data converter for this type of object */
protected var packet: PacketConverter = new ObjectCreateConverter[PlanetSideGameObject]() {}
Name = "object_definition"
private var useRadius: Float = 0f
def UseRadius: Float = useRadius
def UseRadius_=(radius: Float): Float = {
useRadius = radius
UseRadius
}
/**
* Get the conversion object.
* @return

View file

@ -46,8 +46,6 @@ class ProjectileDefinition(objectId: Int)
/** 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
/** radiation clouds create independent damage-dealing areas in a zone that last for the projectile's lifespan */
var radiation_cloud: Boolean = false
//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;

View file

@ -106,8 +106,24 @@ object GeometryForm {
*/
def representByCylinder(radius: Float, height: Float)(o: Any): VolumetricGeometry = {
o match {
case p: PlanetSideGameObject => Cylinder(p.Position, Vector3.relativeUp(p.Orientation), radius, height)
case s: SourceEntry => Cylinder(s.Position, Vector3.relativeUp(s.Orientation), radius, height)
case p: PlanetSideGameObject => Cylinder(p.Position, Vector3.relativeUp(p.Orientation), radius, math.abs(height))
case s: SourceEntry => Cylinder(s.Position, Vector3.relativeUp(s.Orientation), radius, math.abs(height))
case _ => invalidCylinder
}
}
/**
* The geometric representation is a cylinder
* offset with respects to the height of the entity's base.
* @param radius half the distance across
* @param height how tall the cylinder is (the distance of the top to the base)
* @param o the entity
* @return the representation
*/
def representByRaisedCylinder(radius: Float, height: Float)(o: Any): VolumetricGeometry = {
o match {
case p: PlanetSideGameObject => Cylinder(p.Position + Vector3.z(height * 0.5f), Vector3.relativeUp(p.Orientation), radius, math.abs(height))
case s: SourceEntry => Cylinder(s.Position + Vector3.z(height * 0.5f), Vector3.relativeUp(s.Orientation), radius, math.abs(height))
case _ => invalidCylinder
}
}

View file

@ -0,0 +1,31 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.geometry
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.geometry.d2.Rectangle
import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.serverobject.environment.EnvironmentCollision
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
final case class VolumetricEnvironmentCollision(g: VolumetricGeometry)
extends EnvironmentCollision {
private lazy val bound: Rectangle = {
Rectangle(
g.pointOnOutside(Vector3(0, 1,0)).y,
g.pointOnOutside(Vector3(-1,0,0)).x,
g.pointOnOutside(Vector3(0,-1,0)).y,
g.pointOnOutside(Vector3( 1,0,0)).x
)
}
def Geometry: VolumetricGeometry = g
def altitude: Float = g.pointOnOutside(Vector3(0,0,1)).z
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
Zone.distanceCheck(obj.Definition.Geometry(obj), g) <= varDepth
}
def bounding: Rectangle = bound
}

View file

@ -57,6 +57,16 @@ object Segment {
Segment(p, Point(p.x + d.x, p.y + d.y, p.z + d.z))
}
/**
* An overloaded constructor that uses individual coordinates.
* @param a origin
* @param b destination
* @return a `Segment` entity
*/
def apply(a: Vector3, b: Vector3): Segment = {
Segment(Point(a), Point(b))
}
/**
* An overloaded constructor that uses individual coordinates.
* @param x the 'x' coordinate of the position

View file

@ -0,0 +1,330 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.objects.inventory.InventoryTile
object GlobalDefinitionsAmmo {
import GlobalDefinitions._
/**
* Initialize `AmmoBoxDefinition` globals.
*/
final def init(): Unit = {
melee_ammo.Name = "melee_ammo"
melee_ammo.Size = EquipmentSize.Blocked
frag_grenade_ammo.Name = "frag_grenade_ammo"
frag_grenade_ammo.Size = EquipmentSize.Blocked
jammer_grenade_ammo.Name = "jammer_grenade_ammo"
jammer_grenade_ammo.Size = EquipmentSize.Blocked
plasma_grenade_ammo.Name = "plasma_grenade_ammo"
plasma_grenade_ammo.Size = EquipmentSize.Blocked
bullet_9mm.Name = "9mmbullet"
bullet_9mm.Capacity = 50
bullet_9mm.Tile = InventoryTile.Tile33
bullet_9mm_AP.Name = "9mmbullet_AP"
bullet_9mm_AP.Capacity = 50
bullet_9mm_AP.Tile = InventoryTile.Tile33
shotgun_shell.Name = "shotgun_shell"
shotgun_shell.Capacity = 16
shotgun_shell.Tile = InventoryTile.Tile33
shotgun_shell_AP.Name = "shotgun_shell_AP"
shotgun_shell_AP.Capacity = 16
shotgun_shell_AP.Tile = InventoryTile.Tile33
energy_cell.Name = "energy_cell"
energy_cell.Capacity = 50
energy_cell.Tile = InventoryTile.Tile33
anniversary_ammo.Name = "anniversary_ammo"
anniversary_ammo.Capacity = 30
anniversary_ammo.Tile = InventoryTile.Tile33
ancient_ammo_combo.Name = "ancient_ammo_combo"
ancient_ammo_combo.Capacity = 30
ancient_ammo_combo.Tile = InventoryTile.Tile33
maelstrom_ammo.Name = "maelstrom_ammo"
maelstrom_ammo.Capacity = 50
maelstrom_ammo.Tile = InventoryTile.Tile33
phoenix_missile.Name = "phoenix_missile"
phoenix_missile.Size = EquipmentSize.Blocked
striker_missile_ammo.Name = "striker_missile_ammo"
striker_missile_ammo.Capacity = 15
striker_missile_ammo.Tile = InventoryTile.Tile44
hunter_seeker_missile.Name = "hunter_seeker_missile"
hunter_seeker_missile.Capacity = 9
hunter_seeker_missile.Tile = InventoryTile.Tile44
lancer_cartridge.Name = "lancer_cartridge"
lancer_cartridge.Capacity = 18
lancer_cartridge.Tile = InventoryTile.Tile44
rocket.Name = "rocket"
rocket.Capacity = 15
rocket.Tile = InventoryTile.Tile33
frag_cartridge.Name = "frag_cartridge"
frag_cartridge.Capacity = 12
frag_cartridge.Tile = InventoryTile.Tile33
plasma_cartridge.Name = "plasma_cartridge"
plasma_cartridge.Capacity = 12
plasma_cartridge.Tile = InventoryTile.Tile33
jammer_cartridge.Name = "jammer_cartridge"
jammer_cartridge.Capacity = 12
jammer_cartridge.Tile = InventoryTile.Tile33
bolt.Name = "bolt"
bolt.Capacity = 10
bolt.Tile = InventoryTile.Tile33
oicw_ammo.Name = "oicw_ammo"
oicw_ammo.Capacity = 10
oicw_ammo.Tile = InventoryTile.Tile44
flamethrower_ammo.Name = "flamethrower_ammo"
flamethrower_ammo.Capacity = 100
flamethrower_ammo.Tile = InventoryTile.Tile44
winchester_ammo.Name = "winchester_ammo"
winchester_ammo.Capacity = 10
winchester_ammo.Tile = InventoryTile.Tile33
pellet_gun_ammo.Name = "pellet_gun_ammo"
pellet_gun_ammo.Capacity = 8
pellet_gun_ammo.Tile = InventoryTile.Tile33
six_shooter_ammo.Name = "six_shooter_ammo"
six_shooter_ammo.Capacity = 12
six_shooter_ammo.Tile = InventoryTile.Tile33
dualcycler_ammo.Name = "dualcycler_ammo"
dualcycler_ammo.Capacity = 100
dualcycler_ammo.Tile = InventoryTile.Tile44
pounder_ammo.Name = "pounder_ammo"
pounder_ammo.Capacity = 50
pounder_ammo.Tile = InventoryTile.Tile44
burster_ammo.Name = "burster_ammo"
burster_ammo.Capacity = 100
burster_ammo.Tile = InventoryTile.Tile44
scattercannon_ammo.Name = "scattercannon_ammo"
scattercannon_ammo.Capacity = 50
scattercannon_ammo.Tile = InventoryTile.Tile44
falcon_ammo.Name = "falcon_ammo"
falcon_ammo.Capacity = 50
falcon_ammo.Tile = InventoryTile.Tile44
sparrow_ammo.Name = "sparrow_ammo"
sparrow_ammo.Capacity = 50
sparrow_ammo.Tile = InventoryTile.Tile44
quasar_ammo.Name = "quasar_ammo"
quasar_ammo.Capacity = 60
quasar_ammo.Tile = InventoryTile.Tile44
comet_ammo.Name = "comet_ammo"
comet_ammo.Capacity = 50
comet_ammo.Tile = InventoryTile.Tile44
starfire_ammo.Name = "starfire_ammo"
starfire_ammo.Capacity = 50
starfire_ammo.Tile = InventoryTile.Tile44
health_canister.Name = "health_canister"
health_canister.Capacity = 100
health_canister.Tile = InventoryTile.Tile23
armor_canister.Name = "armor_canister"
armor_canister.Capacity = 100
armor_canister.repairAmount = 12f //ADB says 12.5, but 12 is better for the math
armor_canister.Tile = InventoryTile.Tile23
upgrade_canister.Name = "upgrade_canister"
upgrade_canister.Capacity = 1
upgrade_canister.Tile = InventoryTile.Tile23
trek_ammo.Name = "trek_ammo"
trek_ammo.Size = EquipmentSize.Blocked
bullet_35mm.Name = "35mmbullet"
bullet_35mm.Capacity = 100
bullet_35mm.Tile = InventoryTile.Tile44
aphelion_laser_ammo.Name = "aphelion_laser_ammo"
aphelion_laser_ammo.Capacity = 165
aphelion_laser_ammo.Tile = InventoryTile.Tile44
aphelion_immolation_cannon_ammo.Name = "aphelion_immolation_cannon_ammo"
aphelion_immolation_cannon_ammo.Capacity = 100
aphelion_immolation_cannon_ammo.Tile = InventoryTile.Tile55
aphelion_plasma_rocket_ammo.Name = "aphelion_plasma_rocket_ammo"
aphelion_plasma_rocket_ammo.Capacity = 195
aphelion_plasma_rocket_ammo.Tile = InventoryTile.Tile55
aphelion_ppa_ammo.Name = "aphelion_ppa_ammo"
aphelion_ppa_ammo.Capacity = 110
aphelion_ppa_ammo.Tile = InventoryTile.Tile44
aphelion_starfire_ammo.Name = "aphelion_starfire_ammo"
aphelion_starfire_ammo.Capacity = 132
aphelion_starfire_ammo.Tile = InventoryTile.Tile44
skyguard_flak_cannon_ammo.Name = "skyguard_flak_cannon_ammo"
skyguard_flak_cannon_ammo.Capacity = 200
skyguard_flak_cannon_ammo.Tile = InventoryTile.Tile44
firebird_missile.Name = "firebird_missile"
firebird_missile.Capacity = 50
firebird_missile.Tile = InventoryTile.Tile44
flux_cannon_thresher_battery.Name = "flux_cannon_thresher_battery"
flux_cannon_thresher_battery.Capacity = 150
flux_cannon_thresher_battery.Tile = InventoryTile.Tile44
fluxpod_ammo.Name = "fluxpod_ammo"
fluxpod_ammo.Capacity = 80
fluxpod_ammo.Tile = InventoryTile.Tile44
hellfire_ammo.Name = "hellfire_ammo"
hellfire_ammo.Capacity = 24
hellfire_ammo.Tile = InventoryTile.Tile44
liberator_bomb.Name = "liberator_bomb"
liberator_bomb.Capacity = 20
liberator_bomb.Tile = InventoryTile.Tile44
bullet_25mm.Name = "25mmbullet"
bullet_25mm.Capacity = 150
bullet_25mm.Tile = InventoryTile.Tile44
bullet_75mm.Name = "75mmbullet"
bullet_75mm.Capacity = 100
bullet_75mm.Tile = InventoryTile.Tile44
heavy_grenade_mortar.Name = "heavy_grenade_mortar"
heavy_grenade_mortar.Capacity = 100
heavy_grenade_mortar.Tile = InventoryTile.Tile44
pulse_battery.Name = "pulse_battery"
pulse_battery.Capacity = 100
pulse_battery.Tile = InventoryTile.Tile44
heavy_rail_beam_battery.Name = "heavy_rail_beam_battery"
heavy_rail_beam_battery.Capacity = 100
heavy_rail_beam_battery.Tile = InventoryTile.Tile44
reaver_rocket.Name = "reaver_rocket"
reaver_rocket.Capacity = 12
reaver_rocket.Tile = InventoryTile.Tile44
bullet_20mm.Name = "20mmbullet"
bullet_20mm.Capacity = 200
bullet_20mm.Tile = InventoryTile.Tile44
bullet_12mm.Name = "12mmbullet"
bullet_12mm.Capacity = 300
bullet_12mm.Tile = InventoryTile.Tile44
wasp_rocket_ammo.Name = "wasp_rocket_ammo"
wasp_rocket_ammo.Capacity = 6
wasp_rocket_ammo.Tile = InventoryTile.Tile44
wasp_gun_ammo.Name = "wasp_gun_ammo"
wasp_gun_ammo.Capacity = 150
wasp_gun_ammo.Tile = InventoryTile.Tile44
bullet_15mm.Name = "15mmbullet"
bullet_15mm.Capacity = 360
bullet_15mm.Tile = InventoryTile.Tile44
colossus_100mm_cannon_ammo.Name = "colossus_100mm_cannon_ammo"
colossus_100mm_cannon_ammo.Capacity = 90
colossus_100mm_cannon_ammo.Tile = InventoryTile.Tile55
colossus_burster_ammo.Name = "colossus_burster_ammo"
colossus_burster_ammo.Capacity = 235
colossus_burster_ammo.Tile = InventoryTile.Tile44
colossus_cluster_bomb_ammo.Name = "colossus_cluster_bomb_ammo"
colossus_cluster_bomb_ammo.Capacity = 150
colossus_cluster_bomb_ammo.Tile = InventoryTile.Tile55
colossus_chaingun_ammo.Name = "colossus_chaingun_ammo"
colossus_chaingun_ammo.Capacity = 600
colossus_chaingun_ammo.Tile = InventoryTile.Tile44
colossus_tank_cannon_ammo.Name = "colossus_tank_cannon_ammo"
colossus_tank_cannon_ammo.Capacity = 110
colossus_tank_cannon_ammo.Tile = InventoryTile.Tile44
bullet_105mm.Name = "105mmbullet"
bullet_105mm.Capacity = 100
bullet_105mm.Tile = InventoryTile.Tile44
gauss_cannon_ammo.Name = "gauss_cannon_ammo"
gauss_cannon_ammo.Capacity = 15
gauss_cannon_ammo.Tile = InventoryTile.Tile44
peregrine_dual_machine_gun_ammo.Name = "peregrine_dual_machine_gun_ammo"
peregrine_dual_machine_gun_ammo.Capacity = 240
peregrine_dual_machine_gun_ammo.Tile = InventoryTile.Tile44
peregrine_mechhammer_ammo.Name = "peregrine_mechhammer_ammo"
peregrine_mechhammer_ammo.Capacity = 30
peregrine_mechhammer_ammo.Tile = InventoryTile.Tile44
peregrine_particle_cannon_ammo.Name = "peregrine_particle_cannon_ammo"
peregrine_particle_cannon_ammo.Capacity = 40
peregrine_particle_cannon_ammo.Tile = InventoryTile.Tile55
peregrine_rocket_pod_ammo.Name = "peregrine_rocket_pod_ammo"
peregrine_rocket_pod_ammo.Capacity = 275
peregrine_rocket_pod_ammo.Tile = InventoryTile.Tile55
peregrine_sparrow_ammo.Name = "peregrine_sparrow_ammo"
peregrine_sparrow_ammo.Capacity = 150
peregrine_sparrow_ammo.Tile = InventoryTile.Tile44
bullet_150mm.Name = "150mmbullet"
bullet_150mm.Capacity = 50
bullet_150mm.Tile = InventoryTile.Tile44
phalanx_ammo.Name = "phalanx_ammo"
phalanx_ammo.Size = EquipmentSize.Inventory
spitfire_ammo.Name = "spitfire_ammo"
spitfire_ammo.Size = EquipmentSize.Inventory
spitfire_aa_ammo.Name = "spitfire_aa_ammo"
spitfire_aa_ammo.Size = EquipmentSize.Inventory
energy_gun_ammo.Name = "energy_gun_ammo"
energy_gun_ammo.Size = EquipmentSize.Inventory
armor_siphon_ammo.Name = "armor_siphon_ammo"
armor_siphon_ammo.Capacity = 0
armor_siphon_ammo.Size = EquipmentSize.Blocked
ntu_siphon_ammo.Name = "ntu_siphon_ammo"
ntu_siphon_ammo.Capacity = 0
ntu_siphon_ammo.Size = EquipmentSize.Blocked
}
}

View file

@ -0,0 +1,176 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.SpawnPoint
import net.psforever.types.LatticeBenefit
object GlobalDefinitionsBuilding {
import GlobalDefinitions._
/**
* Initialize `BuildingDefinition` globals.
*/
def init(): Unit = {
amp_station.Name = "amp_station"
amp_station.SOIRadius = 300
amp_station.LatticeLinkBenefit = LatticeBenefit.AmpStation
comm_station.Name = "comm_station"
comm_station.SOIRadius = 300
comm_station.LatticeLinkBenefit = LatticeBenefit.InterlinkFacility
comm_station_dsp.Name = "comm_station_dsp"
comm_station_dsp.SOIRadius = 300
comm_station_dsp.LatticeLinkBenefit = LatticeBenefit.DropshipCenter
cryo_facility.Name = "cryo_facility"
cryo_facility.SOIRadius = 300
cryo_facility.LatticeLinkBenefit = LatticeBenefit.BioLaboratory
tech_plant.Name = "tech_plant"
tech_plant.SOIRadius = 300
tech_plant.LatticeLinkBenefit = LatticeBenefit.TechnologyPlant
building.Name = "building"
vanu_core.Name = "vanu_core"
ground_bldg_a.Name = "ground_bldg_a"
ground_bldg_b.Name = "ground_bldg_b"
ground_bldg_c.Name = "ground_bldg_c"
ground_bldg_d.Name = "ground_bldg_d"
ground_bldg_e.Name = "ground_bldg_e"
ground_bldg_f.Name = "ground_bldg_f"
ground_bldg_g.Name = "ground_bldg_g"
ground_bldg_h.Name = "ground_bldg_h"
ground_bldg_i.Name = "ground_bldg_i"
ground_bldg_j.Name = "ground_bldg_j"
ground_bldg_z.Name = "ground_bldg_z"
ceiling_bldg_a.Name = "ceiling_bldg_a"
ceiling_bldg_b.Name = "ceiling_bldg_b"
ceiling_bldg_c.Name = "ceiling_bldg_c"
ceiling_bldg_d.Name = "ceiling_bldg_d"
ceiling_bldg_e.Name = "ceiling_bldg_e"
ceiling_bldg_f.Name = "ceiling_bldg_f"
ceiling_bldg_g.Name = "ceiling_bldg_g"
ceiling_bldg_h.Name = "ceiling_bldg_h"
ceiling_bldg_i.Name = "ceiling_bldg_i"
ceiling_bldg_j.Name = "ceiling_bldg_j"
ceiling_bldg_z.Name = "ceiling_bldg_z"
mainbase1.Name = "mainbase1"
mainbase2.Name = "mainbase2"
mainbase3.Name = "mainbase3"
meeting_center_nc.Name = "meeting_center_nc"
meeting_center_tr.Name = "meeting_center_tr"
meeting_center_vs.Name = "meeting_center_vs"
minibase1.Name = "minibase1"
minibase2.Name = "minibase2"
minibase3.Name = "minibase3"
redoubt.Name = "redoubt"
redoubt.SOIRadius = 187
tower_a.Name = "tower_a"
tower_a.SOIRadius = 50
tower_b.Name = "tower_b"
tower_b.SOIRadius = 50
tower_c.Name = "tower_c"
tower_c.SOIRadius = 50
vanu_control_point.Name = "vanu_control_point"
vanu_control_point.SOIRadius = 187
vanu_vehicle_station.Name = "vanu_vehicle_station"
vanu_vehicle_station.SOIRadius = 187
hst.Name = "hst"
hst.UseRadius = 44.96882005f
hst.SOIRadius = 82
hst.VehicleAllowance = true
hst.NoWarp += dropship
hst.NoWarp += galaxy_gunship
hst.NoWarp += lodestar
hst.NoWarp += aphelion_gunner
hst.NoWarp += aphelion_flight
hst.NoWarp += colossus_gunner
hst.NoWarp += colossus_flight
hst.NoWarp += peregrine_gunner
hst.NoWarp += peregrine_flight
hst.SpecificPointFunc = SpawnPoint.CavernGate(innerRadius = 6f)
warpgate.Name = "warpgate"
warpgate.UseRadius = 67.81070029f
warpgate.SOIRadius = 302 //301.8713f
warpgate.VehicleAllowance = true
warpgate.SpecificPointFunc = SpawnPoint.Gate
warpgate_cavern.Name = "warpgate_cavern"
warpgate_cavern.UseRadius = 19.72639434f
warpgate_cavern.SOIRadius = 41
warpgate_cavern.VehicleAllowance = true
warpgate_cavern.SpecificPointFunc = SpawnPoint.CavernGate(innerRadius = 4.5f)
warpgate_small.Name = "warpgate_small"
warpgate_small.UseRadius = 69.03687655f
warpgate_small.SOIRadius = 103
warpgate_small.VehicleAllowance = true
warpgate_small.SpecificPointFunc = SpawnPoint.SmallGate(innerRadius = 27.60654127f, flightlessZOffset = 0.5f)
bunker_gauntlet.Name = "bunker_gauntlet"
bunker_lg.Name = "bunker_lg"
bunker_sm.Name = "bunker_sm"
orbital_building_nc.Name = "orbital_building_nc"
orbital_building_tr.Name = "orbital_building_tr"
orbital_building_vs.Name = "orbital_building_vs"
VT_building_nc.Name = "VT_building_nc"
VT_building_tr.Name = "VT_building_tr"
VT_building_vs.Name = "VT_building_vs"
vt_dropship.Name = "vt_dropship"
vt_spawn.Name = "vt_spawn"
vt_vehicle.Name = "vt_vehicle"
}
}

View file

@ -0,0 +1,461 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.ce.DeployableCategory
import net.psforever.objects.definition.DeployAnimation
import net.psforever.objects.definition.converter.{FieldTurretConverter, InternalTelepadDeployableConverter, TelepadDeployableConverter}
import net.psforever.objects.equipment.{EffectTarget, TargetValidation}
import net.psforever.objects.geometry.GeometryForm
import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.serverobject.mount.{MountInfo, SeatDefinition}
import net.psforever.objects.serverobject.turret.{AutoChecks, AutoCooldowns, AutoRanges, Automation, TurretUpgrade}
import net.psforever.objects.vital.{CollisionXYData, CollisionZData, ComplexDeployableResolutions, SimpleResolutions}
import net.psforever.objects.vital.base.DamageType
import net.psforever.objects.vital.collision.TrapCollisionDamageMultiplier
import net.psforever.objects.vital.etc.ExplodingRadialDegrade
import net.psforever.objects.vital.prop.DamageWithPosition
import scala.collection.mutable
import scala.concurrent.duration._
object GlobalDefinitionsDeployable {
import GlobalDefinitions._
/**
* Initialize `Deployable` globals.
*/
def init(): Unit = {
val mine: Any => VolumetricGeometry = GeometryForm.representByCylinder(radius = 0.1914f, height = 0.0957f)
val smallTurret: Any => VolumetricGeometry = GeometryForm.representByCylinder(radius = 0.48435f, height = 1.23438f)
val sensor: Any => VolumetricGeometry = GeometryForm.representByCylinder(radius = 0.1914f, height = 1.21875f)
val largeTurret: Any => VolumetricGeometry = GeometryForm.representByCylinder(radius = 0.8437f, height = 2.29687f)
boomer.Name = "boomer"
boomer.Descriptor = "Boomers"
boomer.MaxHealth = 100
boomer.Damageable = true
boomer.DamageableByFriendlyFire = false
boomer.Repairable = false
boomer.DeployCategory = DeployableCategory.Boomers
boomer.DeployTime = Duration.create(1000, "ms")
boomer.deployAnimation = DeployAnimation.Standard
boomer.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.Splash
SympatheticExplosion = true
Damage0 = 250
Damage1 = 750
Damage2 = 400
Damage3 = 400
Damage4 = 1850
DamageRadius = 5.1f
DamageAtEdge = 0.1f
Modifiers = ExplodingRadialDegrade
}
boomer.Geometry = mine
he_mine.Name = "he_mine"
he_mine.Descriptor = "Mines"
he_mine.MaxHealth = 100
he_mine.Damageable = true
he_mine.DamageableByFriendlyFire = false
he_mine.Repairable = false
he_mine.DeployTime = Duration.create(1000, "ms")
he_mine.deployAnimation = DeployAnimation.Standard
he_mine.triggerRadius = 3f
he_mine.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.Splash
SympatheticExplosion = true
Damage0 = 100
Damage1 = 750
Damage2 = 400
Damage3 = 565
Damage4 = 1600
DamageRadius = 6.6f
DamageAtEdge = 0.25f
Modifiers = ExplodingRadialDegrade
}
he_mine.Geometry = mine
jammer_mine.Name = "jammer_mine"
jammer_mine.Descriptor = "JammerMines"
jammer_mine.MaxHealth = 100
jammer_mine.Damageable = true
jammer_mine.DamageableByFriendlyFire = false
jammer_mine.Repairable = false
jammer_mine.DeployTime = Duration.create(1000, "ms")
jammer_mine.deployAnimation = DeployAnimation.Standard
jammer_mine.DetonateOnJamming = false
jammer_mine.triggerRadius = 3f
jammer_mine.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.Splash
Damage0 = 0
DamageRadius = 10f
DamageAtEdge = 1.0f
AdditionalEffect = true
JammedEffectDuration += TargetValidation(
EffectTarget.Category.Player,
EffectTarget.Validation.Player
) -> 1000
JammedEffectDuration += TargetValidation(
EffectTarget.Category.Vehicle,
EffectTarget.Validation.AMS
) -> 5000
JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.MotionSensor
) -> 30000
JammedEffectDuration += TargetValidation(
EffectTarget.Category.Deployable,
EffectTarget.Validation.Spitfire
) -> 30000
JammedEffectDuration += TargetValidation(
EffectTarget.Category.Turret,
EffectTarget.Validation.Turret
) -> 30000
JammedEffectDuration += TargetValidation(
EffectTarget.Category.Vehicle,
EffectTarget.Validation.VehicleNotAMS
) -> 10000
}
jammer_mine.Geometry = mine
spitfire_turret.Name = "spitfire_turret"
spitfire_turret.Descriptor = "Spitfires"
spitfire_turret.MaxHealth = 100
spitfire_turret.Damageable = true
spitfire_turret.Repairable = true
spitfire_turret.RepairIfDestroyed = false
spitfire_turret.WeaponPaths += 1 -> new mutable.HashMap()
spitfire_turret.WeaponPaths(1) += TurretUpgrade.None -> spitfire_weapon
spitfire_turret.ReserveAmmunition = false
spitfire_turret.DeployCategory = DeployableCategory.SmallTurrets
spitfire_turret.DeployTime = Duration.create(5000, "ms")
spitfire_turret.Model = ComplexDeployableResolutions.calculate
spitfire_turret.deployAnimation = DeployAnimation.Standard
spitfire_turret.AutoFire = Automation(
AutoRanges(
detection = 75f,
trigger = 50f,
escape = 50f
),
AutoChecks(
validation = List(
EffectTarget.Validation.SmallRoboticsTurretValidatePlayerTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateMaxTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateGroundVehicleTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget,
EffectTarget.Validation.AutoTurretValidateMountableEntityTarget
)
),
retaliatoryDelay = 2000L, //8000L
refireTime = 200.milliseconds //150.milliseconds
)
spitfire_turret.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 200
Damage1 = 300
DamageRadius = 8
DamageAtEdge = 0.2f
Modifiers = ExplodingRadialDegrade
}
spitfire_turret.Geometry = smallTurret
spitfire_turret.collision.xy = CollisionXYData(Array((0.01f, 10), (0.02f, 40), (0.03f, 60), (0.04f, 80), (0.05f, 100)))
spitfire_turret.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
spitfire_turret.mass = 5f
spitfire_cloaked.Name = "spitfire_cloaked"
spitfire_cloaked.Descriptor = "CloakingSpitfires"
spitfire_cloaked.MaxHealth = 100
spitfire_cloaked.Damageable = true
spitfire_cloaked.Repairable = true
spitfire_cloaked.RepairIfDestroyed = false
spitfire_cloaked.WeaponPaths += 1 -> new mutable.HashMap()
spitfire_cloaked.WeaponPaths(1) += TurretUpgrade.None -> spitfire_weapon
spitfire_cloaked.ReserveAmmunition = false
spitfire_cloaked.DeployCategory = DeployableCategory.SmallTurrets
spitfire_cloaked.DeployTime = Duration.create(5000, "ms")
spitfire_cloaked.deployAnimation = DeployAnimation.Standard
spitfire_cloaked.Model = ComplexDeployableResolutions.calculate
spitfire_cloaked.AutoFire = Automation(
AutoRanges(
detection = 75f,
trigger = 50f,
escape = 75f
),
AutoChecks(
validation = List(
EffectTarget.Validation.SmallRoboticsTurretValidatePlayerTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateMaxTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateGroundVehicleTarget,
EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget,
EffectTarget.Validation.AutoTurretValidateMountableEntityTarget
)
),
cooldowns = AutoCooldowns(
targetSelect = 0L,
missedShot = 0L
),
detectionSweepTime = 500.milliseconds,
retaliatoryDelay = 1L, //8000L
retaliationOverridesTarget = false,
refireTime = 200.milliseconds //150.milliseconds
)
spitfire_cloaked.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 50
Damage1 = 75
DamageRadius = 8
DamageAtEdge = 0.2f
Modifiers = ExplodingRadialDegrade
}
spitfire_cloaked.Geometry = smallTurret
spitfire_cloaked.collision.xy = CollisionXYData(Array((0.01f, 10), (0.02f, 40), (0.03f, 60), (0.04f, 80), (0.05f, 100)))
spitfire_cloaked.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
spitfire_cloaked.mass = 5f
spitfire_aa.Name = "spitfire_aa"
spitfire_aa.Descriptor = "FlakSpitfires"
spitfire_aa.MaxHealth = 100
spitfire_aa.Damageable = true
spitfire_aa.Repairable = true
spitfire_aa.RepairIfDestroyed = false
spitfire_aa.WeaponPaths += 1 -> new mutable.HashMap()
spitfire_aa.WeaponPaths(1) += TurretUpgrade.None -> spitfire_aa_weapon
spitfire_aa.ReserveAmmunition = false
spitfire_aa.DeployCategory = DeployableCategory.SmallTurrets
spitfire_aa.DeployTime = Duration.create(5000, "ms")
spitfire_aa.deployAnimation = DeployAnimation.Standard
spitfire_aa.Model = ComplexDeployableResolutions.calculate
spitfire_aa.AutoFire = Automation(
AutoRanges(
detection = 125f,
trigger = 100f,
escape = 200f
),
AutoChecks(
validation = List(EffectTarget.Validation.SmallRoboticsTurretValidateAircraftTarget)
),
retaliatoryDelay = 2000L, //8000L
retaliationOverridesTarget = false,
refireTime = 0.seconds, //300.milliseconds
cylindrical = true,
cylindricalExtraHeight = 50f
)
spitfire_aa.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 200
Damage1 = 300
DamageRadius = 8
DamageAtEdge = 0.2f
Modifiers = ExplodingRadialDegrade
}
spitfire_aa.Geometry = smallTurret
spitfire_aa.collision.xy = CollisionXYData(Array((0.01f, 10), (0.02f, 40), (0.03f, 60), (0.04f, 80), (0.05f, 100)))
spitfire_aa.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
spitfire_aa.mass = 5f
motionalarmsensor.Name = "motionalarmsensor"
motionalarmsensor.Descriptor = "MotionSensors"
motionalarmsensor.MaxHealth = 100
motionalarmsensor.Damageable = true
motionalarmsensor.Repairable = true
motionalarmsensor.RepairIfDestroyed = false
motionalarmsensor.DeployTime = Duration.create(1000, "ms")
motionalarmsensor.deployAnimation = DeployAnimation.Standard
motionalarmsensor.Geometry = sensor
sensor_shield.Name = "sensor_shield"
sensor_shield.Descriptor = "SensorShields"
sensor_shield.MaxHealth = 100
sensor_shield.Damageable = true
sensor_shield.Repairable = true
sensor_shield.RepairIfDestroyed = false
sensor_shield.DeployTime = Duration.create(5000, "ms")
sensor_shield.deployAnimation = DeployAnimation.Standard
sensor_shield.Geometry = sensor
tank_traps.Name = "tank_traps"
tank_traps.Descriptor = "TankTraps"
tank_traps.MaxHealth = 5000
tank_traps.Damageable = true
tank_traps.Repairable = true
tank_traps.RepairIfDestroyed = false
tank_traps.DeployCategory = DeployableCategory.TankTraps
tank_traps.DeployTime = Duration.create(6000, "ms")
tank_traps.deployAnimation = DeployAnimation.Fdu
//tank_traps do not explode
tank_traps.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 10
Damage1 = 10
DamageRadius = 8
DamageAtEdge = 0.2f
Modifiers = ExplodingRadialDegrade
}
tank_traps.Geometry = GeometryForm.representByCylinder(radius = 2.89680997f, height = 3.57812f)
tank_traps.collision.xy = CollisionXYData(Array((0.01f, 5), (0.02f, 10), (0.03f, 15), (0.04f, 20), (0.05f, 25)))
tank_traps.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
tank_traps.Modifiers = TrapCollisionDamageMultiplier(5f) //10f
tank_traps.mass = 600f
val fieldTurretConverter = new FieldTurretConverter
portable_manned_turret.Name = "portable_manned_turret"
portable_manned_turret.Descriptor = "FieldTurrets"
portable_manned_turret.MaxHealth = 1000
portable_manned_turret.Damageable = true
portable_manned_turret.Repairable = true
portable_manned_turret.RepairIfDestroyed = false
portable_manned_turret.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret.WeaponPaths(1) += TurretUpgrade.None -> energy_gun
portable_manned_turret.Seats += 0 -> new SeatDefinition()
portable_manned_turret.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret.MountPoints += 1 -> MountInfo(0)
portable_manned_turret.MountPoints += 2 -> MountInfo(0)
portable_manned_turret.ReserveAmmunition = true
portable_manned_turret.FactionLocked = true
portable_manned_turret.Packet = fieldTurretConverter
portable_manned_turret.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret.DeployTime = Duration.create(6000, "ms")
portable_manned_turret.deployAnimation = DeployAnimation.Fdu
portable_manned_turret.Model = ComplexDeployableResolutions.calculate
portable_manned_turret.RadiationShielding = 0.5f
portable_manned_turret.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 150
Damage1 = 300
DamageRadius = 8
DamageAtEdge = 0.1f
Modifiers = ExplodingRadialDegrade
}
portable_manned_turret.Geometry = largeTurret
portable_manned_turret.collision.xy = CollisionXYData(Array((0.01f, 10), (0.02f, 40), (0.03f, 60), (0.04f, 80), (0.05f, 100)))
portable_manned_turret.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
portable_manned_turret.mass = 100f
portable_manned_turret_nc.Name = "portable_manned_turret_nc"
portable_manned_turret_nc.Descriptor = "FieldTurrets"
portable_manned_turret_nc.MaxHealth = 1000
portable_manned_turret_nc.Damageable = true
portable_manned_turret_nc.Repairable = true
portable_manned_turret_nc.RepairIfDestroyed = false
portable_manned_turret_nc.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_nc.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_nc
portable_manned_turret_nc.Seats += 0 -> new SeatDefinition()
portable_manned_turret_nc.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret_nc.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_nc.MountPoints += 2 -> MountInfo(0)
portable_manned_turret_nc.ReserveAmmunition = true
portable_manned_turret_nc.FactionLocked = true
portable_manned_turret_nc.Packet = fieldTurretConverter
portable_manned_turret_nc.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret_nc.DeployTime = Duration.create(6000, "ms")
portable_manned_turret_nc.deployAnimation = DeployAnimation.Fdu
portable_manned_turret_nc.Model = ComplexDeployableResolutions.calculate
portable_manned_turret_nc.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 150
Damage1 = 300
DamageRadius = 8
DamageAtEdge = 0.1f
Modifiers = ExplodingRadialDegrade
}
portable_manned_turret_nc.Geometry = largeTurret
portable_manned_turret_nc.collision.xy = CollisionXYData(Array((0.01f, 10), (0.02f, 40), (0.03f, 60), (0.04f, 80), (0.05f, 100)))
portable_manned_turret_nc.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
portable_manned_turret_nc.mass = 100f
portable_manned_turret_tr.Name = "portable_manned_turret_tr"
portable_manned_turret_tr.Descriptor = "FieldTurrets"
portable_manned_turret_tr.MaxHealth = 1000
portable_manned_turret_tr.Damageable = true
portable_manned_turret_tr.Repairable = true
portable_manned_turret_tr.RepairIfDestroyed = false
portable_manned_turret_tr.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_tr.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_tr
portable_manned_turret_tr.Seats += 0 -> new SeatDefinition()
portable_manned_turret_tr.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret_tr.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_tr.MountPoints += 2 -> MountInfo(0)
portable_manned_turret_tr.ReserveAmmunition = true
portable_manned_turret_tr.FactionLocked = true
portable_manned_turret_tr.Packet = fieldTurretConverter
portable_manned_turret_tr.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret_tr.DeployTime = Duration.create(6000, "ms")
portable_manned_turret_tr.deployAnimation = DeployAnimation.Fdu
portable_manned_turret_tr.Model = ComplexDeployableResolutions.calculate
portable_manned_turret_tr.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 150
Damage1 = 300
DamageRadius = 8
DamageAtEdge = 0.1f
Modifiers = ExplodingRadialDegrade
}
portable_manned_turret_tr.Geometry = largeTurret
portable_manned_turret_tr.collision.xy = CollisionXYData(Array((0.01f, 10), (0.02f, 40), (0.03f, 60), (0.04f, 80), (0.05f, 100)))
portable_manned_turret_tr.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
portable_manned_turret_tr.mass = 100f
portable_manned_turret_vs.Name = "portable_manned_turret_vs"
portable_manned_turret_vs.Descriptor = "FieldTurrets"
portable_manned_turret_vs.MaxHealth = 1000
portable_manned_turret_vs.Damageable = true
portable_manned_turret_vs.Repairable = true
portable_manned_turret_vs.RepairIfDestroyed = false
portable_manned_turret_vs.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_vs.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_vs
portable_manned_turret_vs.Seats += 0 -> new SeatDefinition()
portable_manned_turret_vs.controlledWeapons(seat = 0, weapon = 1)
portable_manned_turret_vs.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_vs.MountPoints += 2 -> MountInfo(0)
portable_manned_turret_vs.ReserveAmmunition = true
portable_manned_turret_vs.FactionLocked = true
portable_manned_turret_vs.Packet = fieldTurretConverter
portable_manned_turret_vs.DeployCategory = DeployableCategory.FieldTurrets
portable_manned_turret_vs.DeployTime = Duration.create(6000, "ms")
portable_manned_turret_vs.deployAnimation = DeployAnimation.Fdu
portable_manned_turret_vs.Model = ComplexDeployableResolutions.calculate
portable_manned_turret_vs.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 150
Damage1 = 300
DamageRadius = 8
DamageAtEdge = 0.1f
Modifiers = ExplodingRadialDegrade
}
portable_manned_turret_vs.Geometry = largeTurret
portable_manned_turret_vs.collision.xy = CollisionXYData(Array((0.01f, 10), (0.02f, 40), (0.03f, 60), (0.04f, 80), (0.05f, 100)))
portable_manned_turret_vs.collision.z = CollisionZData(Array((4f, 10), (4.25f, 40), (4.5f, 60), (4.75f, 80), (5f, 100)))
portable_manned_turret_vs.mass = 100f
deployable_shield_generator.Name = "deployable_shield_generator"
deployable_shield_generator.Descriptor = "ShieldGenerators"
deployable_shield_generator.MaxHealth = 1700
deployable_shield_generator.Damageable = true
deployable_shield_generator.Repairable = true
deployable_shield_generator.RepairIfDestroyed = false
deployable_shield_generator.DeployTime = Duration.create(6000, "ms")
deployable_shield_generator.deployAnimation = DeployAnimation.Fdu
deployable_shield_generator.Model = ComplexDeployableResolutions.calculate
deployable_shield_generator.Geometry = GeometryForm.representByCylinder(radius = 0.6562f, height = 2.17188f)
router_telepad_deployable.Name = "router_telepad_deployable"
router_telepad_deployable.MaxHealth = 100
router_telepad_deployable.Damageable = true
router_telepad_deployable.Repairable = false
router_telepad_deployable.DeployTime = Duration.create(1, "ms")
router_telepad_deployable.DeployCategory = DeployableCategory.Telepads
router_telepad_deployable.Packet = new TelepadDeployableConverter
router_telepad_deployable.Model = SimpleResolutions.calculate
router_telepad_deployable.Geometry = GeometryForm.representByRaisedSphere(radius = 1.2344f)
internal_router_telepad_deployable.Name = "router_telepad_deployable"
internal_router_telepad_deployable.MaxHealth = 1
internal_router_telepad_deployable.Damageable = false
internal_router_telepad_deployable.Repairable = false
internal_router_telepad_deployable.DeployTime = Duration.create(1, "ms")
internal_router_telepad_deployable.DeployCategory = DeployableCategory.Telepads
internal_router_telepad_deployable.Packet = new InternalTelepadDeployableConverter
}
}

View file

@ -0,0 +1,111 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.avatar.Certification
import net.psforever.objects.definition.SpecialExoSuitDefinition
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.vital.MaxResolutions
import net.psforever.objects.vital.damage.DamageCalculations
object GlobalDefinitionsExoSuit {
import GlobalDefinitions._
/**
* Initialize `ExoSuitType` globals.
*/
def init(): Unit = {
Standard.Name = "standard"
Standard.MaxArmor = 50
Standard.InventoryScale = InventoryTile.Tile96
Standard.InventoryOffset = 6
Standard.Holster(0, EquipmentSize.Pistol)
Standard.Holster(2, EquipmentSize.Rifle)
Standard.Holster(4, EquipmentSize.Melee)
Standard.ResistanceDirectHit = 4
Standard.ResistanceSplash = 15
Standard.ResistanceAggravated = 8
Standard.collision.forceFactor = 1.5f
Standard.collision.massFactor = 2f
Agile.Name = "lite_armor"
Agile.Descriptor = "agile"
Agile.MaxArmor = 100
Agile.InventoryScale = InventoryTile.Tile99
Agile.InventoryOffset = 6
Agile.Holster(0, EquipmentSize.Pistol)
Agile.Holster(1, EquipmentSize.Pistol)
Agile.Holster(2, EquipmentSize.Rifle)
Agile.Holster(4, EquipmentSize.Melee)
Agile.ResistanceDirectHit = 6
Agile.ResistanceSplash = 25
Agile.ResistanceAggravated = 10
Agile.collision.forceFactor = 1.5f
Agile.collision.massFactor = 2f
Reinforced.Name = "med_armor"
Reinforced.Descriptor = "reinforced"
Reinforced.Permissions = List(Certification.ReinforcedExoSuit)
Reinforced.MaxArmor = 200
Reinforced.InventoryScale = InventoryTile.Tile1209
Reinforced.InventoryOffset = 6
Reinforced.Holster(0, EquipmentSize.Pistol)
Reinforced.Holster(1, EquipmentSize.Pistol)
Reinforced.Holster(2, EquipmentSize.Rifle)
Reinforced.Holster(3, EquipmentSize.Rifle)
Reinforced.Holster(4, EquipmentSize.Melee)
Reinforced.ResistanceDirectHit = 10
Reinforced.ResistanceSplash = 35
Reinforced.ResistanceAggravated = 12
Reinforced.collision.forceFactor = 2f
Reinforced.collision.massFactor = 3f
Infiltration.Name = "infiltration_suit"
Infiltration.Permissions = List(Certification.InfiltrationSuit)
Infiltration.MaxArmor = 0
Infiltration.InventoryScale = InventoryTile.Tile66
Infiltration.InventoryOffset = 6
Infiltration.Holster(0, EquipmentSize.Pistol)
Infiltration.Holster(4, EquipmentSize.Melee)
def CommonMaxConfig(max: SpecialExoSuitDefinition): Unit = {
max.Permissions = List(Certification.AIMAX, Certification.AVMAX, Certification.AAMAX, Certification.UniMAX)
max.MaxArmor = 650
max.InventoryScale = InventoryTile.Tile1612
max.InventoryOffset = 6
max.Holster(0, EquipmentSize.Max)
max.Holster(4, EquipmentSize.Melee)
max.Subtract.Damage1 = 2
max.ResistanceDirectHit = 6
max.ResistanceSplash = 35
max.ResistanceAggravated = 10
max.RadiationShielding = 0.5f
max.collision.forceFactor = 4f
max.collision.massFactor = 10f
max.DamageUsing = DamageCalculations.AgainstMaxSuit
max.Model = MaxResolutions.calculate
}
CommonMaxConfig(VSMAX)
VSMAX.Name = "vshev"
VSMAX.MaxCapacitor = 50
VSMAX.CapacitorRechargeDelayMillis = 5000
VSMAX.CapacitorRechargePerSecond = 3
VSMAX.CapacitorDrainPerSecond = 20
CommonMaxConfig(TRMAX)
TRMAX.Name = "trhev"
TRMAX.MaxCapacitor = 300
TRMAX.CapacitorRechargeDelayMillis = 10000
TRMAX.CapacitorRechargePerSecond = 10
TRMAX.CapacitorDrainPerSecond = 30
CommonMaxConfig(NCMAX)
NCMAX.Name = "nchev"
NCMAX.MaxCapacitor = 400
NCMAX.CapacitorRechargeDelayMillis = 10000
NCMAX.CapacitorRechargePerSecond = 4
NCMAX.CapacitorDrainPerSecond = 4
}
}

View file

@ -0,0 +1,61 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
import net.psforever.types.ExoSuitType
object GlobalDefinitionsImplant {
import GlobalDefinitions._
/**
* Initialize `ImplantDefinition` globals.
*/
def init(): Unit = {
advanced_regen.Name = "advanced_regen"
advanced_regen.InitializationDuration = 120
advanced_regen.StaminaCost = 2
advanced_regen.CostIntervalDefault = 500
targeting.Name = "targeting"
targeting.InitializationDuration = 60
audio_amplifier.Name = "audio_amplifier"
audio_amplifier.InitializationDuration = 60
audio_amplifier.StaminaCost = 1
audio_amplifier.CostIntervalDefault = 1000
darklight_vision.Name = "darklight_vision"
darklight_vision.InitializationDuration = 60
darklight_vision.ActivationStaminaCost = 3
darklight_vision.StaminaCost = 1
darklight_vision.CostIntervalDefault = 500
melee_booster.Name = "melee_booster"
melee_booster.InitializationDuration = 120
melee_booster.StaminaCost = 10
personal_shield.Name = "personal_shield"
personal_shield.InitializationDuration = 120
personal_shield.StaminaCost = 1
personal_shield.CostIntervalDefault = 600
range_magnifier.Name = "range_magnifier"
range_magnifier.InitializationDuration = 60
second_wind.Name = "second_wind"
second_wind.InitializationDuration = 180
silent_run.Name = "silent_run"
silent_run.InitializationDuration = 90
silent_run.StaminaCost = 1
silent_run.CostIntervalDefault = 333
silent_run.CostIntervalByExoSuitHashMap(ExoSuitType.Infiltration) = 1000
surge.Name = "surge"
surge.InitializationDuration = 90
surge.StaminaCost = 1
surge.CostIntervalDefault = 1000
surge.CostIntervalByExoSuitHashMap(ExoSuitType.Agile) = 500
surge.CostIntervalByExoSuitHashMap(ExoSuitType.Reinforced) = 333
}
}

View file

@ -0,0 +1,21 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
object GlobalDefinitionsKit {
import GlobalDefinitions._
/**
* Initialize `KitDefinition` globals.
*/
def init(): Unit = {
medkit.Name = "medkit"
super_medkit.Name = "super_medkit"
super_armorkit.Name = "super_armorkit"
super_staminakit.Name = "super_staminakit"
}
}

View file

@ -0,0 +1,938 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.{GlobalDefinitions, SpawnPoint}
import net.psforever.objects.avatar.Certification
import net.psforever.objects.equipment.EffectTarget
import net.psforever.objects.geometry.GeometryForm
import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.serverobject.doors.InteriorDoorField
import net.psforever.objects.serverobject.mount.{MountInfo, SeatDefinition}
import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition
import net.psforever.objects.serverobject.structures.AutoRepairStats
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalDefinition
import net.psforever.objects.serverobject.terminals.{EquipmentTerminalDefinition, VehicleTerminalDefinition}
import net.psforever.objects.serverobject.terminals.tabs.{ArmorWithAmmoPage, BattleframeSpawnLoadoutPage, CavernEquipmentQuestion, CavernVehicleQuestion, CertificationPage, EquipmentPage, ImplantPage, InfantryLoadoutPage, NoCavernEquipmentRule, NoExoSuitRule, VehicleLoadoutPage, VehiclePage}
import net.psforever.objects.serverobject.turret.{AutoChecks, AutoRanges, Automation, TurretUpgrade}
import net.psforever.objects.vital.base.DamageType
import net.psforever.objects.vital.etc.ExplodingRadialDegrade
import net.psforever.objects.vital.prop.DamageWithPosition
import net.psforever.types.{ExoSuitType, Vector3}
import scala.collection.mutable
import scala.concurrent.duration._
object GlobalDefinitionsMiscellaneous {
import GlobalDefinitions._
/**
* Initialize `Miscellaneous` globals.
*/
def init(): Unit = {
val vterm: Any => VolumetricGeometry = GeometryForm.representByCylinder(radius = 1.03515f, height = 1.09374f)
val certs: Seq[Certification] = Certification.values.filter(_.cost != 0)
val page: CertificationPage = CertificationPage(certs)
ams_respawn_tube.Name = "ams_respawn_tube"
ams_respawn_tube.Delay = 10 // Temporary -- Default value is 15
ams_respawn_tube.SpecificPointFunc = SpawnPoint.AMS
ams_respawn_tube.Damageable = false
ams_respawn_tube.Repairable = false
matrix_terminala.Name = "matrix_terminala"
matrix_terminala.Damageable = false
matrix_terminala.Repairable = false
matrix_terminalb.Name = "matrix_terminalb"
matrix_terminalb.Damageable = false
matrix_terminalb.Repairable = false
matrix_terminalc.Name = "matrix_terminalc"
matrix_terminalc.Damageable = false
matrix_terminalc.Repairable = false
spawn_terminal.Name = "spawn_terminal"
spawn_terminal.Damageable = false
spawn_terminal.Repairable = false
spawn_terminal.autoRepair = AutoRepairStats(1, 5000, 200, 1)
order_terminal.Name = "order_terminal"
order_terminal.Tab += 0 -> {
val tab = EquipmentPage(
EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
order_terminal.Tab += 1 -> ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits ++ EquipmentTerminalDefinition.maxSuits,
EquipmentTerminalDefinition.maxAmmo
)
order_terminal.Tab += 2 -> EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
order_terminal.Tab += 3 -> EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
order_terminal.Tab += 4 -> {
val tab = InfantryLoadoutPage()
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
order_terminal.SellEquipmentByDefault = true
order_terminal.MaxHealth = 500
order_terminal.Damageable = true
order_terminal.Repairable = true
order_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
order_terminal.RepairIfDestroyed = true
order_terminal.Subtract.Damage1 = 8
order_terminal.Geometry = GeometryForm.representByCylinder(radius = 0.8438f, height = 1.3f)
order_terminala.Name = "order_terminala"
order_terminala.Tab += 0 -> {
val tab = EquipmentPage(
EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
)
tab.Exclude = List(NoCavernEquipmentRule)
tab
}
order_terminala.Tab += 1 -> ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits,
EquipmentTerminalDefinition.maxAmmo
)
order_terminala.Tab += 2 -> EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
order_terminala.Tab += 3 -> EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
order_terminala.Tab += 4 -> {
val tab = InfantryLoadoutPage()
tab.Exclude = List(NoExoSuitRule(ExoSuitType.MAX), NoCavernEquipmentRule)
tab
}
order_terminala.SellEquipmentByDefault = true
order_terminala.Damageable = false
order_terminala.Repairable = false
order_terminalb.Name = "order_terminalb"
order_terminalb.Tab += 0 -> {
val tab = EquipmentPage(
EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
)
tab.Exclude = List(NoCavernEquipmentRule)
tab
}
order_terminalb.Tab += 1 -> ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits,
EquipmentTerminalDefinition.maxAmmo
)
order_terminalb.Tab += 2 -> EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
order_terminalb.Tab += 3 -> EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
order_terminalb.Tab += 4 -> {
val tab = InfantryLoadoutPage()
tab.Exclude = List(NoExoSuitRule(ExoSuitType.MAX), NoCavernEquipmentRule)
tab
}
order_terminalb.SellEquipmentByDefault = true
order_terminalb.Damageable = false
order_terminalb.Repairable = false
vanu_equipment_term.Name = "vanu_equipment_term"
vanu_equipment_term.Tab += 0 -> EquipmentPage(
EquipmentTerminalDefinition.infantryAmmunition ++ EquipmentTerminalDefinition.infantryWeapons
)
vanu_equipment_term.Tab += 1 -> ArmorWithAmmoPage(
EquipmentTerminalDefinition.suits ++ EquipmentTerminalDefinition.maxSuits,
EquipmentTerminalDefinition.maxAmmo
)
vanu_equipment_term.Tab += 2 -> EquipmentPage(
EquipmentTerminalDefinition.supportAmmunition ++ EquipmentTerminalDefinition.supportWeapons
)
vanu_equipment_term.Tab += 3 -> EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
vanu_equipment_term.Tab += 4 -> InfantryLoadoutPage()
vanu_equipment_term.SellEquipmentByDefault = true
vanu_equipment_term.Damageable = false
vanu_equipment_term.Repairable = false
cert_terminal.Name = "cert_terminal"
cert_terminal.Tab += 0 -> page
cert_terminal.MaxHealth = 500
cert_terminal.Damageable = true
cert_terminal.Repairable = true
cert_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
cert_terminal.RepairIfDestroyed = true
cert_terminal.Subtract.Damage1 = 8
cert_terminal.Geometry = GeometryForm.representByCylinder(radius = 0.66405f, height = 1.09374f)
implant_terminal_mech.Name = "implant_terminal_mech"
implant_terminal_mech.MaxHealth = 1500 //TODO 1000; right now, 1000 (mech) + 500 (interface)
implant_terminal_mech.Damageable = true
implant_terminal_mech.Repairable = true
implant_terminal_mech.autoRepair = AutoRepairStats(1.6f, 5000, 2400, 0.05f)
implant_terminal_mech.RepairIfDestroyed = true
implant_terminal_mech.RadiationShielding = 0.5f
implant_terminal_mech.Geometry = GeometryForm.representByCylinder(radius = 2.7813f, height = 6.4375f)
implant_terminal_interface.Name = "implant_terminal_interface"
implant_terminal_interface.Tab += 0 -> ImplantPage(ImplantTerminalDefinition.implants)
implant_terminal_interface.MaxHealth = 500
implant_terminal_interface.Damageable = false //TODO true
implant_terminal_interface.Repairable = true
implant_terminal_interface.autoRepair = AutoRepairStats(1, 5000, 200, 1)
implant_terminal_interface.RepairIfDestroyed = true
//TODO will need geometry when Damageable = true
ground_vehicle_terminal.Name = "ground_vehicle_terminal"
ground_vehicle_terminal.Tab += 46769 -> {
val tab = VehiclePage(
VehicleTerminalDefinition.groundVehicles,
VehicleTerminalDefinition.trunk
)
tab.Exclude = List(CavernVehicleQuestion)
tab
}
ground_vehicle_terminal.Tab += 4 -> {
val tab = VehicleLoadoutPage(10)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
ground_vehicle_terminal.MaxHealth = 500
ground_vehicle_terminal.Damageable = true
ground_vehicle_terminal.Repairable = true
ground_vehicle_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
ground_vehicle_terminal.RepairIfDestroyed = true
ground_vehicle_terminal.Subtract.Damage1 = 8
ground_vehicle_terminal.Geometry = vterm
air_vehicle_terminal.Name = "air_vehicle_terminal"
air_vehicle_terminal.Tab += 46769 -> VehiclePage(
VehicleTerminalDefinition.flight1Vehicles,
VehicleTerminalDefinition.trunk
)
air_vehicle_terminal.Tab += 4 -> {
val tab = VehicleLoadoutPage(10)
tab.Exclude = List(CavernVehicleQuestion, CavernEquipmentQuestion)
tab
}
air_vehicle_terminal.MaxHealth = 500
air_vehicle_terminal.Damageable = true
air_vehicle_terminal.Repairable = true
air_vehicle_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
air_vehicle_terminal.RepairIfDestroyed = true
air_vehicle_terminal.Subtract.Damage1 = 8
air_vehicle_terminal.Geometry = vterm
dropship_vehicle_terminal.Name = "dropship_vehicle_terminal"
dropship_vehicle_terminal.Tab += 46769 -> VehiclePage(
VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.flight2Vehicles,
VehicleTerminalDefinition.trunk
)
dropship_vehicle_terminal.Tab += 4 -> {
val tab = VehicleLoadoutPage(10)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
dropship_vehicle_terminal.MaxHealth = 500
dropship_vehicle_terminal.Damageable = true
dropship_vehicle_terminal.Repairable = true
dropship_vehicle_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
dropship_vehicle_terminal.RepairIfDestroyed = true
dropship_vehicle_terminal.Subtract.Damage1 = 8
dropship_vehicle_terminal.Geometry = vterm
vehicle_terminal_combined.Name = "vehicle_terminal_combined"
vehicle_terminal_combined.Tab += 46769 -> {
val tab = VehiclePage(
VehicleTerminalDefinition.flight1Vehicles ++ VehicleTerminalDefinition.groundVehicles,
VehicleTerminalDefinition.trunk
)
tab.Exclude = List(CavernVehicleQuestion)
tab
}
vehicle_terminal_combined.Tab += 4 -> {
val tab = VehicleLoadoutPage(10)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
vehicle_terminal_combined.MaxHealth = 500
vehicle_terminal_combined.Damageable = true
vehicle_terminal_combined.Repairable = true
vehicle_terminal_combined.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
vehicle_terminal_combined.RepairIfDestroyed = true
vehicle_terminal_combined.Subtract.Damage1 = 8
vehicle_terminal_combined.Geometry = vterm
vanu_air_vehicle_term.Name = "vanu_air_vehicle_term"
vanu_air_vehicle_term.Tab += 46769 -> VehiclePage(
VehicleTerminalDefinition.flight1Vehicles,
VehicleTerminalDefinition.trunk
)
vanu_air_vehicle_term.Tab += 4 -> VehicleLoadoutPage(10)
vanu_air_vehicle_term.MaxHealth = 500
vanu_air_vehicle_term.Damageable = true
vanu_air_vehicle_term.Repairable = true
vanu_air_vehicle_term.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
vanu_air_vehicle_term.RepairIfDestroyed = true
vanu_air_vehicle_term.Subtract.Damage1 = 8
vanu_vehicle_term.Name = "vanu_vehicle_term"
vanu_vehicle_term.Tab += 46769 -> VehiclePage(
VehicleTerminalDefinition.groundVehicles,
VehicleTerminalDefinition.trunk
)
vanu_vehicle_term.Tab += 4 -> VehicleLoadoutPage(10)
vanu_vehicle_term.MaxHealth = 500
vanu_vehicle_term.Damageable = true
vanu_vehicle_term.Repairable = true
vanu_vehicle_term.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
vanu_vehicle_term.RepairIfDestroyed = true
vanu_vehicle_term.Subtract.Damage1 = 8
bfr_terminal.Name = "bfr_terminal"
bfr_terminal.Tab += 0 -> VehiclePage(
VehicleTerminalDefinition.bfrVehicles,
VehicleTerminalDefinition.trunk
)
bfr_terminal.Tab += 1 -> EquipmentPage(
EquipmentTerminalDefinition.bfrAmmunition ++ EquipmentTerminalDefinition.bfrArmWeapons
) //inaccessible?
bfr_terminal.Tab += 2 -> EquipmentPage(
EquipmentTerminalDefinition.bfrAmmunition ++ EquipmentTerminalDefinition.bfrGunnerWeapons
) //inaccessible?
bfr_terminal.Tab += 3 -> {
val tab = BattleframeSpawnLoadoutPage(VehicleTerminalDefinition.bfrVehicles)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
bfr_terminal.MaxHealth = 500
bfr_terminal.Damageable = true
bfr_terminal.Repairable = true
bfr_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
bfr_terminal.RepairIfDestroyed = true
bfr_terminal.Subtract.Damage1 = 8
bfr_terminal.Geometry = GeometryForm.representByCylinder(radius = 0.92185f, height = 2.64693f)
respawn_tube.Name = "respawn_tube"
respawn_tube.Delay = 10
respawn_tube.SpecificPointFunc = SpawnPoint.Tube
respawn_tube.MaxHealth = 1000
respawn_tube.Damageable = true
respawn_tube.DamageableByFriendlyFire = false
respawn_tube.Repairable = true
respawn_tube.autoRepair = AutoRepairStats(1.6f, 10000, 2400, 1)
respawn_tube.RepairIfDestroyed = true
respawn_tube.Subtract.Damage1 = 8
respawn_tube.Geometry = GeometryForm.representByCylinder(radius = 0.9336f, height = 2.84375f)
respawn_tube_sanctuary.Name = "respawn_tube"
respawn_tube_sanctuary.Delay = 10
respawn_tube_sanctuary.SpecificPointFunc = SpawnPoint.Default
respawn_tube_sanctuary.MaxHealth = 1000
respawn_tube_sanctuary.Damageable = false //true?
respawn_tube_sanctuary.DamageableByFriendlyFire = false
respawn_tube_sanctuary.Repairable = true
respawn_tube_sanctuary.autoRepair = AutoRepairStats(1.6f, 10000, 2400, 1)
//TODO will need geometry when Damageable = true
respawn_tube_tower.Name = "respawn_tube_tower"
respawn_tube_tower.Delay = 10 // Temporary -- Default value is 20
respawn_tube_tower.SpecificPointFunc = SpawnPoint.Tube
respawn_tube_tower.MaxHealth = 1000
respawn_tube_tower.Damageable = true
respawn_tube_tower.DamageableByFriendlyFire = false
respawn_tube_tower.Repairable = true
respawn_tube_tower.autoRepair = AutoRepairStats(1.6f, 10000, 2400, 1)
respawn_tube_tower.RepairIfDestroyed = true
respawn_tube_tower.Subtract.Damage1 = 8
respawn_tube_tower.Geometry = GeometryForm.representByCylinder(radius = 0.9336f, height = 2.84375f)
teleportpad_terminal.Name = "teleportpad_terminal"
teleportpad_terminal.Tab += 0 -> EquipmentPage(EquipmentTerminalDefinition.routerTerminal)
teleportpad_terminal.Damageable = false
teleportpad_terminal.Repairable = false
targeting_laser_dispenser.Name = "targeting_laser_dispenser"
targeting_laser_dispenser.Tab += 0 -> EquipmentPage(EquipmentTerminalDefinition.flailTerminal)
targeting_laser_dispenser.Damageable = false
targeting_laser_dispenser.Repairable = false
medical_terminal.Name = "medical_terminal"
medical_terminal.Interval = 500
medical_terminal.HealAmount = 5
medical_terminal.ArmorAmount = 10
medical_terminal.UseRadius = 0.75f
medical_terminal.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.Medical
medical_terminal.MaxHealth = 500
medical_terminal.Damageable = true
medical_terminal.Repairable = true
medical_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
medical_terminal.RepairIfDestroyed = true
medical_terminal.Geometry = GeometryForm.representByCylinder(radius = 0.711f, height = 1.75f)
adv_med_terminal.Name = "adv_med_terminal"
adv_med_terminal.Interval = 500
adv_med_terminal.HealAmount = 8
adv_med_terminal.ArmorAmount = 15
adv_med_terminal.UseRadius = 0.75f
adv_med_terminal.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.Medical
adv_med_terminal.MaxHealth = 750
adv_med_terminal.Damageable = true
adv_med_terminal.Repairable = true
adv_med_terminal.autoRepair = AutoRepairStats(1.57894f, 5000, 2400, 0.05f)
adv_med_terminal.RepairIfDestroyed = true
adv_med_terminal.Geometry = GeometryForm.representByCylinder(radius = 0.8662125f, height = 3.47f)
crystals_health_a.Name = "crystals_health_a"
crystals_health_a.Interval = 500
crystals_health_a.HealAmount = 4
crystals_health_a.UseRadius = 5
crystals_health_a.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.HealthCrystal
crystals_health_a.Damageable = false
crystals_health_a.Repairable = false
crystals_health_b.Name = "crystals_health_b"
crystals_health_b.Interval = 500
crystals_health_b.HealAmount = 4
crystals_health_b.UseRadius = 5
crystals_health_b.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.HealthCrystal
crystals_health_b.Damageable = false
crystals_health_b.Repairable = false
crystals_repair_a.Name = "crystals_repair_a"
crystals_repair_a.Interval = 500
crystals_repair_a.ArmorAmount = 4
crystals_repair_a.UseRadius = 5
crystals_repair_a.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.RepairCrystal
crystals_repair_a.Damageable = false
crystals_repair_a.Repairable = false
crystals_repair_b.Name = "crystals_repair_b"
crystals_repair_b.Interval = 500
crystals_repair_b.ArmorAmount = 4
crystals_repair_b.UseRadius = 5
crystals_repair_b.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.RepairCrystal
crystals_repair_b.Damageable = false
crystals_repair_b.Repairable = false
crystals_vehicle_a.Name = "crystals_vehicle_a"
crystals_vehicle_a.Interval = 1000
crystals_vehicle_a.HealAmount = 60
crystals_vehicle_a.UseRadius = 15
crystals_vehicle_a.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.VehicleCrystal
crystals_vehicle_a.Damageable = false
crystals_vehicle_a.Repairable = false
crystals_vehicle_b.Name = "crystals_vehicle_b"
crystals_vehicle_b.Interval = 1000
crystals_vehicle_b.HealAmount = 60
crystals_vehicle_b.UseRadius = 15
crystals_vehicle_b.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.VehicleCrystal
crystals_vehicle_b.Damageable = false
crystals_vehicle_b.Repairable = false
crystals_energy_a.Name = "crystals_energy_a"
crystals_energy_a.Interval = 1000
crystals_energy_a.UseRadius = 5
crystals_energy_a.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.AncientWeaponRecharge
crystals_energy_a.Damageable = false
crystals_energy_a.Repairable = false
crystals_energy_b.Name = "crystals_energy_b"
crystals_energy_b.Interval = 1000
crystals_energy_b.UseRadius = 5
crystals_energy_b.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.AncientWeaponRecharge
crystals_energy_b.Damageable = false
crystals_energy_b.Repairable = false
crystals_energy.Name = "crystals_energy"
crystals_energy.Interval = 1000
crystals_energy.UseRadius = 5
crystals_energy.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.AncientWeaponRecharge
crystals_energy.Damageable = false
crystals_energy.Repairable = false
portable_med_terminal.Name = "portable_med_terminal"
portable_med_terminal.Interval = 500
portable_med_terminal.HealAmount = 5
portable_med_terminal.ArmorAmount = 10
portable_med_terminal.UseRadius = 3
portable_med_terminal.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.Medical
portable_med_terminal.MaxHealth = 500
portable_med_terminal.Damageable = false //TODO actually true
portable_med_terminal.Repairable = false
portable_med_terminal.autoRepair = AutoRepairStats(2.24215f, 5000, 3500, 0.05f)
pad_landing_frame.Name = "pad_landing_frame"
pad_landing_frame.Interval = 1000
pad_landing_frame.HealAmount = 60
pad_landing_frame.UseRadius = 20
pad_landing_frame.TargetValidation += EffectTarget.Category.Aircraft -> EffectTarget.Validation.PadLanding
pad_landing_frame.Damageable = false
pad_landing_frame.Repairable = false
pad_landing_tower_frame.Name = "pad_landing_tower_frame"
pad_landing_tower_frame.Interval = 1000
pad_landing_tower_frame.HealAmount = 60
pad_landing_tower_frame.UseRadius = 20
pad_landing_tower_frame.TargetValidation += EffectTarget.Category.Aircraft -> EffectTarget.Validation.PadLanding
pad_landing_tower_frame.Damageable = false
pad_landing_tower_frame.Repairable = false
repair_silo.Name = "repair_silo"
repair_silo.Interval = 1000
repair_silo.HealAmount = 60
repair_silo.UseRadius = 20
repair_silo.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.RepairSilo
repair_silo.Damageable = false
repair_silo.Repairable = false
recharge_terminal.Name = "recharge_terminal"
recharge_terminal.Interval = 1000
recharge_terminal.UseRadius = 20
recharge_terminal.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.AncientVehicleWeaponRecharge
recharge_terminal.Damageable = false
recharge_terminal.Repairable = false
recharge_terminal_weapon_module.Name = "recharge_terminal_weapon_module"
recharge_terminal_weapon_module.Interval = 1000
recharge_terminal_weapon_module.UseRadius = 300
recharge_terminal_weapon_module.TargetValidation += EffectTarget.Category.Player -> EffectTarget.Validation.AncientWeaponRecharge
recharge_terminal_weapon_module.Damageable = false
recharge_terminal_weapon_module.Repairable = false
mb_pad_creation.Name = "mb_pad_creation"
mb_pad_creation.Damageable = false
mb_pad_creation.Repairable = false
mb_pad_creation.VehicleCreationZOffset = 2.52604f
mb_pad_creation.killBox = VehicleSpawnPadDefinition.prepareKillBox(
forwardLimit = 14,
backLimit = 10,
sideLimit = 7.5f,
aboveLimit = 5 //double to 10 when spawning a flying vehicle
)
mb_pad_creation.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 99999
DamageRadiusMin = 14
DamageRadius = 14.5f
DamageAtEdge = 0.00002f
//damage is 99999 at 14m, dropping rapidly to ~1 at 14.5m
}
dropship_pad_doors.Name = "dropship_pad_doors"
dropship_pad_doors.Damageable = false
dropship_pad_doors.Repairable = false
dropship_pad_doors.VehicleCreationZOffset = 4.89507f
dropship_pad_doors.VehicleCreationZOrientOffset = -90f
dropship_pad_doors.killBox = VehicleSpawnPadDefinition.prepareKillBox(
forwardLimit = 14,
backLimit = 14,
sideLimit = 13.5f,
aboveLimit = 5 //doubles to 10
)
dropship_pad_doors.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 99999
DamageRadiusMin = 14
DamageRadius = 14.5f
DamageAtEdge = 0.00002f
//damage is 99999 at 14m, dropping rapidly to ~1 at 14.5m
}
vanu_vehicle_creation_pad.Name = "vanu_vehicle_creation_pad"
vanu_vehicle_creation_pad.Damageable = false
vanu_vehicle_creation_pad.Repairable = false
vanu_vehicle_creation_pad.killBox = VehicleSpawnPadDefinition.prepareVanuKillBox(
radius = 8.5f,
aboveLimit = 5
)
vanu_vehicle_creation_pad.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 99999
DamageRadiusMin = 14
DamageRadius = 14.5f
DamageAtEdge = 0.00002f
//damage is 99999 at 14m, dropping rapidly to ~1 at 14.5m
}
bfr_door.Name = "bfr_door"
bfr_door.Damageable = false
bfr_door.Repairable = false
//bfr_door.VehicleCreationZOffset = -4.5f
bfr_door.VehicleCreationZOrientOffset = 0f //90f
bfr_door.killBox = VehicleSpawnPadDefinition.prepareBfrShedKillBox(
radius = 10f,
aboveLimit = 10f
)
bfr_door.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 99999
DamageRadiusMin = 14 //TODO fix this
DamageRadius = 14.5f //TODO fix this
DamageAtEdge = 0.00002f
//damage is 99999 at 14m, dropping rapidly to ~1 at 14.5m
}
pad_create.Name = "pad_create"
pad_create.Damageable = false
pad_create.Repairable = false
//pad_create.killBox = ...
//pad_create.innateDamage = ...
pad_creation.Name = "pad_creation"
pad_creation.Damageable = false
pad_creation.Repairable = false
pad_creation.VehicleCreationZOffset = 1.70982f
//pad_creation.killBox = ...
//pad_creation.innateDamage = ...
spawnpoint_vehicle.Name = "spawnpoint_vehicle"
spawnpoint_vehicle.Damageable = false
spawnpoint_vehicle.Repairable = false
//spawnpoint_vehicle.killBox = ...
//spawnpoint_vehicle.innateDamage = ...
mb_locker.Name = "mb_locker"
mb_locker.Damageable = false
mb_locker.Repairable = false
lock_external.Name = "lock_external"
lock_external.Damageable = false
lock_external.Repairable = false
amp_cap_door.Name = "amp_cap_door"
ancient_door.Name = "ancient_door"
ancient_door.UseRadius = 1f
//ancient_door.environmentField = InteriorDoorField()
ancient_garage_door.Name = "ancient_garage_door"
ancient_garage_door.UseRadius = 1f
//ancient_garage_door.environmentField = InteriorDoorField()
cryo_med_door.Name = "cryo_med_door"
cryo_room_door.Name = "cryo_room_door"
door.Name = "door"
door_airlock.Name = "door_airlock"
door_airlock_orb.Name = "door_airlock_orb"
door_dsp.Name = "door_dsp"
door_garage.Name = "door_garage"
door_interior.Name = "door_interior"
door_mb.Name = "door_mb"
door_mb_garage.Name = "door_mb_garage"
door_mb_main.Name = "door_mb_main"
door_mb_orb.Name = "door_mb_orb"
door_mb_side.Name = "door_mb_side"
door_nc_garage.Name = "door_nc_garage"
door_nc_rotating.Name = "door_nc_rotating"
door_ncside.Name = "door_ncside"
door_orbspawn.Name = "door_orbspawn"
door_spawn_mb.Name = "door_spawn_mb"
door_spawn_mb.Damageable = true
garage_door.Name = "garage_door"
gr_door_airlock.Name = "gr_door_airlock"
gr_door_ext.Name = "gr_door_ext"
gr_door_ext.UseRadius = 1.9f
gr_door_ext.environmentField = InteriorDoorField()
gr_door_garage_ext.Name = "gr_door_garage_ext"
gr_door_garage_ext.UseRadius = 11f
gr_door_garage_ext.initialOpeningDistance = 8f
gr_door_garage_ext.continuousOpenDistance = 9f
gr_door_garage_ext.environmentField = InteriorDoorField(Some(-11), centerOn = true)
gr_door_garage_int.Name = "gr_door_garage_int"
gr_door_garage_int.initialOpeningDistance = 8f
gr_door_garage_int.continuousOpenDistance = 9f
gr_door_int.Name = "gr_door_int"
gr_door_main.Name = "gr_door_main"
gr_door_main.UseRadius = 2.75f
gr_door_main.environmentField = InteriorDoorField()
gr_door_mb_ext.Name = "gr_door_mb_ext"
gr_door_mb_ext.UseRadius = 2f
gr_door_mb_ext.environmentField = InteriorDoorField()
gr_door_mb_int.Name = "gr_door_mb_int"
gr_door_mb_lrg.Name = "gr_door_mb_lrg"
gr_door_mb_lrg.UseRadius = 2.5f
gr_door_mb_lrg.environmentField = InteriorDoorField()
gr_door_mb_obsd.Name = "gr_door_mb_obsd"
gr_door_mb_orb.Name = "gr_door_mb_orb"
gr_door_med.Name = "gr_door_med"
main_door.Name = "main_door"
shield_door.Name = "shield_door"
spawn_tube_door.Name = "spawn_tube_door"
spawn_tube_door.Damageable = true
spawn_tube_door_coffin.Name = "spawn_tube_door_coffin"
spawn_tube_door_coffin.Damageable = true
resource_silo.Name = "resource_silo"
resource_silo.Damageable = false
resource_silo.Repairable = false
resource_silo.MaxNtuCapacitor = 1000
resource_silo.ChargeTime = 105.seconds //from 0-100% in roughly 105s on live (~20%-100% https://youtu.be/veOWToR2nSk?t=1402)
capture_terminal.Name = "capture_terminal"
capture_terminal.Damageable = false
capture_terminal.Repairable = false
capture_terminal.FacilityHackTime = 15.minutes
secondary_capture.Name = "secondary_capture"
secondary_capture.Damageable = false
secondary_capture.Repairable = false
secondary_capture.FacilityHackTime = 1.millisecond
vanu_control_console.Name = "vanu_control_console"
vanu_control_console.Damageable = false
vanu_control_console.Repairable = false
vanu_control_console.FacilityHackTime = 10.minutes
lodestar_repair_terminal.Name = "lodestar_repair_terminal"
lodestar_repair_terminal.Interval = 1000
lodestar_repair_terminal.HealAmount = 60
lodestar_repair_terminal.UseRadius = 40
lodestar_repair_terminal.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.LodestarRepair
lodestar_repair_terminal.Damageable = false
lodestar_repair_terminal.Repairable = false
multivehicle_rearm_terminal.Name = "multivehicle_rearm_terminal"
multivehicle_rearm_terminal.Tab += 3 -> EquipmentPage(
EquipmentTerminalDefinition.vehicleAmmunition
)
multivehicle_rearm_terminal.Tab += 4 -> {
val tab = VehicleLoadoutPage(10)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
multivehicle_rearm_terminal.SellEquipmentByDefault = true //TODO ?
multivehicle_rearm_terminal.Damageable = false
multivehicle_rearm_terminal.Repairable = false
bfr_rearm_terminal.Name = "bfr_rearm_terminal"
bfr_rearm_terminal.Tab += 1 -> EquipmentPage(
EquipmentTerminalDefinition.bfrAmmunition ++ EquipmentTerminalDefinition.bfrArmWeapons
)
bfr_rearm_terminal.Tab += 2 -> EquipmentPage(
EquipmentTerminalDefinition.bfrAmmunition ++ EquipmentTerminalDefinition.bfrGunnerWeapons
)
bfr_rearm_terminal.Tab += 3 -> {
val tab = VehicleLoadoutPage(15)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
bfr_rearm_terminal.SellEquipmentByDefault = true //TODO ?
bfr_rearm_terminal.Damageable = false
bfr_rearm_terminal.Repairable = false
air_rearm_terminal.Name = "air_rearm_terminal"
air_rearm_terminal.Tab += 3 -> EquipmentPage(EquipmentTerminalDefinition.vehicleAmmunition)
air_rearm_terminal.Tab += 4 -> {
val tab = VehicleLoadoutPage(10)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
air_rearm_terminal.SellEquipmentByDefault = true //TODO ?
air_rearm_terminal.Damageable = false
air_rearm_terminal.Repairable = false
ground_rearm_terminal.Name = "ground_rearm_terminal"
ground_rearm_terminal.Tab += 3 -> EquipmentPage(
EquipmentTerminalDefinition.vehicleAmmunition
)
ground_rearm_terminal.Tab += 4 -> {
val tab = VehicleLoadoutPage(10)
tab.Exclude = List(CavernEquipmentQuestion)
tab
}
ground_rearm_terminal.SellEquipmentByDefault = true //TODO ?
ground_rearm_terminal.Damageable = false
ground_rearm_terminal.Repairable = false
manned_turret.Name = "manned_turret"
manned_turret.MaxHealth = 3600
manned_turret.Damageable = true
manned_turret.DamageDisablesAt = 1800
manned_turret.Repairable = true
manned_turret.autoRepair = AutoRepairStats(1.0909f, 10000, 1600, 0.05f)
manned_turret.RepairIfDestroyed = true
manned_turret.WeaponPaths += 1 -> new mutable.HashMap()
manned_turret.WeaponPaths(1) += TurretUpgrade.None -> phalanx_sgl_hevgatcan
manned_turret.WeaponPaths(1) += TurretUpgrade.AVCombo -> phalanx_avcombo
manned_turret.WeaponPaths(1) += TurretUpgrade.FlakCombo -> phalanx_flakcombo
manned_turret.Seats += 0 -> new SeatDefinition()
manned_turret.controlledWeapons(seat = 0, weapon = 1)
manned_turret.MountPoints += 1 -> MountInfo(0)
manned_turret.FactionLocked = true
manned_turret.ReserveAmmunition = false
manned_turret.RadiationShielding = 0.5f
manned_turret.AutoFire = Automation(
AutoRanges(
detection = 125f,
trigger = 100f,
escape = 200f
),
AutoChecks(
validation = List(
EffectTarget.Validation.FacilityTurretValidateMaxTarget,
EffectTarget.Validation.FacilityTurretValidateGroundVehicleTarget,
EffectTarget.Validation.FacilityTurretValidateAircraftTarget,
EffectTarget.Validation.AutoTurretValidateMountableEntityTarget
)
),
retaliatoryDelay = 4000L, //8000L
cylindrical = true,
cylindricalExtraHeight = 50f,
detectionSweepTime = 2.seconds,
refireTime = 362.milliseconds //312.milliseconds
)
manned_turret.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 150
Damage1 = 300
DamageRadius = 5
DamageAtEdge = 0.1f
Modifiers = ExplodingRadialDegrade
}
manned_turret.Geometry = GeometryForm.representByCylinder(radius = 1.2695f, height = 4.042f)
vanu_sentry_turret.Name = "vanu_sentry_turret"
vanu_sentry_turret.MaxHealth = 1500
vanu_sentry_turret.Damageable = true
vanu_sentry_turret.DamageDisablesAt = 0
vanu_sentry_turret.Repairable = true
vanu_sentry_turret.autoRepair = AutoRepairStats(3.27272f, 10000, 1000, 0.05f)
vanu_sentry_turret.RepairIfDestroyed = true
vanu_sentry_turret.WeaponPaths += 1 -> new mutable.HashMap()
vanu_sentry_turret.WeaponPaths(1) += TurretUpgrade.None -> vanu_sentry_turret_weapon
vanu_sentry_turret.Seats += 0 -> new SeatDefinition()
vanu_sentry_turret.controlledWeapons(seat = 0, weapon = 1)
vanu_sentry_turret.MountPoints += 1 -> MountInfo(0)
vanu_sentry_turret.MountPoints += 2 -> MountInfo(0)
vanu_sentry_turret.FactionLocked = false
vanu_sentry_turret.ReserveAmmunition = false
vanu_sentry_turret.Geometry = GeometryForm.representByCylinder(radius = 1.76311f, height = 3.984375f)
painbox.Name = "painbox"
painbox.alwaysOn = false
painbox.sphereOffset = Vector3(0, 0, -0.4f)
painbox.Damageable = false
painbox.Repairable = false
painbox.innateDamage = new DamageWithPosition {
Damage0 = 2
DamageRadius = 0
DamageToHealthOnly = true
}
painbox_continuous.Name = "painbox_continuous"
painbox_continuous.sphereOffset = Vector3(0, 0, -0.4f)
painbox_continuous.Damageable = false
painbox_continuous.Repairable = false
painbox_continuous.innateDamage = new DamageWithPosition {
Damage0 = 2
DamageRadius = 0
DamageToHealthOnly = true
}
painbox_door_radius.Name = "painbox_door_radius"
painbox_door_radius.alwaysOn = false
painbox_door_radius.sphereOffset = Vector3(0, 0, -0.4f)
painbox_door_radius.hasNearestDoorDependency = true
painbox_door_radius.Damageable = false
painbox_door_radius.Repairable = false
painbox_door_radius.innateDamage = new DamageWithPosition {
Damage0 = 2
DamageRadius = 10f * 0.6928f
DamageToHealthOnly = true
}
painbox_door_radius_continuous.Name = "painbox_door_radius_continuous"
painbox_door_radius_continuous.sphereOffset = Vector3(0, 0, -0.4f)
painbox_door_radius_continuous.hasNearestDoorDependency = true
painbox_door_radius_continuous.Damageable = false
painbox_door_radius_continuous.Repairable = false
painbox_door_radius_continuous.innateDamage = new DamageWithPosition {
Damage0 = 2
DamageRadius = 10f * 0.6928f
DamageToHealthOnly = true
}
painbox_radius.Name = "painbox_radius"
painbox_radius.alwaysOn = false
painbox_radius.sphereOffset = Vector3(0, 0, -0.4f)
painbox_radius.Damageable = false
painbox_radius.Repairable = false
painbox_radius.innateDamage = new DamageWithPosition {
Damage0 = 2
DamageRadius = 10f * 0.6928f
DamageToHealthOnly = true
}
painbox_radius_continuous.Name = "painbox_radius_continuous"
painbox_radius_continuous.Damageable = false
painbox_radius_continuous.Repairable = false
painbox_radius_continuous.innateDamage = new DamageWithPosition {
Damage0 = 2
DamageRadius = 8.55f
DamageToHealthOnly = true
}
gen_control.Name = "gen_control"
gen_control.Damageable = false
gen_control.Repairable = false
generator.Name = "generator"
generator.MaxHealth = 4000
generator.Damageable = true
generator.DamageableByFriendlyFire = false
generator.Repairable = true
generator.autoRepair = AutoRepairStats(0.77775f, 5000, 875, 1)
generator.RepairDistance = 13.5f
generator.RepairIfDestroyed = true
generator.Subtract.Damage1 = 9
generator.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.One
Damage0 = 99999
DamageRadiusMin = 15
DamageRadius = 15.1f
DamageAtEdge = 0.000011f
Modifiers = ExplodingRadialDegrade
//damage is 99999 at 15m, dropping rapidly to ~1 at 15.1m
}
generator.Geometry = GeometryForm.representByCylinder(radius = 1.2617f, height = 9.14063f)
obbasemesh.Name = "obbasemesh"
obbasemesh.Descriptor = "orbital_shuttle_pad"
obbasemesh.Damageable = false
obbasemesh.Repairable = false
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,8 +3,10 @@ package net.psforever.objects.serverobject.doors
import net.psforever.objects.Player
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.packet.game.UseItemMessage
import net.psforever.types.Vector3
/**
* A structure-owned server object that is a "door" that can open and can close.
@ -12,6 +14,14 @@ import net.psforever.packet.game.UseItemMessage
*/
class Door(private val ddef: DoorDefinition) extends Amenity {
private var openState: Option[Player] = None
private var outwards: Option[Vector3] = None
/*
* Door sidedness does not reflect on the actual sidedness of anything that is in the door.
* While sidedness will be correct for internal doors -
* they are always passages between different between interior rooms in a facility -
* external doors cross between the outside world and a interior room of a facility.
*/
WhichSide = Sidedness.InsideOf
def isOpen: Boolean = openState.isDefined
@ -26,6 +36,17 @@ class Door(private val ddef: DoorDefinition) extends Amenity {
Open
}
def Outwards: Vector3 = outwards.getOrElse(Orientation)
def Outwards_=(out: Vector3): Vector3 = {
Outwards_=(Some(out))
}
def Outwards_=(out: Option[Vector3]): Vector3 = {
outwards = out
Outwards
}
/** Doors do not have health, so only check if they are damageable. */
override def CanDamage : Boolean = Definition.Damageable

View file

@ -1,7 +1,24 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.doors
import net.psforever.objects.serverobject.structures.AmenityDefinition
import net.psforever.objects.geometry.GeometryForm
import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, PieceOfEnvironment}
import net.psforever.objects.serverobject.structures.{Amenity, AmenityDefinition, CreateEnvironmentField}
final case class InteriorDoorField(
cylinderHeight: Option[Float] = None,
centerOn: Boolean = false
) extends CreateEnvironmentField {
def attribute: EnvironmentTrait = EnvironmentAttribute.InteriorField
def create(obj: Amenity): PieceOfEnvironment = {
obj match {
case door: Door => InteriorDoorPassage(door, cylinderHeight, centerOn)
case _ => throw new IllegalArgumentException("expecting door")
}
}
}
/**
* The definition for any `Door`.
@ -14,4 +31,6 @@ class DoorDefinition(objectId: Int)
var initialOpeningDistance: Float = 7.5f
/** range within which the door must detect a target player to remain open */
var continuousOpenDistance: Float = 5.05f
override def Geometry: Any => VolumetricGeometry = GeometryForm.representBySphere(UseRadius)
}

View file

@ -0,0 +1,30 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.doors
import net.psforever.objects.geometry.{GeometryForm, VolumetricEnvironmentCollision}
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentCollision, EnvironmentTrait, PieceOfEnvironment}
final case class InteriorDoorPassage(
door: Door,
cylinderHeight: Option[Float] = None,
centerOn: Boolean = false
)
extends PieceOfEnvironment {
assert(door.Definition.UseRadius > 0f, s"door ${door.GUID} needs an interaction radius to be positive")
private lazy val collisionObject = {
val radius = door.Definition.UseRadius
val g = (cylinderHeight, centerOn) match {
case (Some(h), false) => GeometryForm.representByCylinder(radius, h) _
case (Some(h), true) => GeometryForm.representByRaisedCylinder(radius, h) _
case (None, false) => GeometryForm.representBySphereOnBase(radius) _
case _ => GeometryForm.representBySphere(radius) _
}
VolumetricEnvironmentCollision(g.apply(door))
}
/** a general description of this environment */
override def attribute: EnvironmentTrait = EnvironmentAttribute.InteriorField
/** a special representation of the region that qualifies as "this environment" */
override def collision: EnvironmentCollision = collisionObject
}

View file

@ -0,0 +1,90 @@
// Copyright (c) 2020-2024 PSForever
package net.psforever.objects.serverobject.environment
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.vital.Vitality
import net.psforever.types.Vector3
/**
* A general description of environment and its interactive possibilities.
*/
abstract class EnvironmentTrait {
def canInteractWith(obj: PlanetSideGameObject): Boolean
}
object EnvironmentAttribute {
case object Water extends EnvironmentTrait {
/** water can only interact with objects that are negatively affected by being exposed to water;
* it's better this way */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj.Definition.DrownAtMaxDepth ||
obj.Definition.DisableAtMaxDepth ||
canInteractWithPlayersAndVehicles(obj) ||
(obj match {
case p: Player => p.VehicleSeated.isEmpty
case v: Vehicle => v.MountedIn.isEmpty
case _ => false
})
}
}
case object Lava extends EnvironmentTrait {
/** lava can only interact with anything capable of registering damage */
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithDamagingFields(obj)
}
case object Death extends EnvironmentTrait {
/** death can only interact with anything capable of registering damage */
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithDamagingFields(obj)
}
case object GantryDenialField
extends EnvironmentTrait {
/** only interact with living player characters */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj match {
case p: Player => p.isAlive
case _ => false
}
}
}
case object MovementFieldTrigger
extends EnvironmentTrait {
/** only interact with living player characters or vehicles */
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithPlayersAndVehicles(obj)
}
case object InteriorField
extends EnvironmentTrait {
/** only interact with living player characters or vehicles */
def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithPlayersAndVehicles(obj)
}
/**
* This environment field only interacts with anything capable of registering damage.
* Also, exclude targets that are located at the game world origin.
* @param obj target entity
* @return whether or not this field affects the target entity
*/
def canInteractWithPlayersAndVehicles(obj: PlanetSideGameObject): Boolean = {
(obj.Position != Vector3.Zero) ||
(obj match {
case p: Player => p.isAlive
case v: Vehicle => !v.Destroyed
case _ => false
})
}
/**
* This environment field only interacts with living player characters or not-destroyed vehicles.
* @param obj target entity
* @return whether or not this field affects the target entity
*/
def canInteractWithDamagingFields(obj: PlanetSideGameObject): Boolean = {
obj match {
case o: Vitality => !o.Destroyed && o.Definition.Damageable
case _ => false
}
}
}

View file

@ -1,6 +1,7 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.environment
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.geometry.d2.Rectangle
import net.psforever.types.Vector3
@ -13,14 +14,22 @@ trait EnvironmentCollision {
/** in general, the highest point in this geometry */
def altitude: Float
/**
* Is the test point "within" the bounds of the represented environment?
* @param obj entity to test
* @return `true`, if the point is sufficiently "deep";
* `false`, otherwise
*/
def testInteraction(obj: PlanetSideGameObject): Boolean = testInteraction(obj, varDepth = 0)
/**
* Is the test point "within" the bounds of the represented environment?
* @param pos the test point
* @param obj entity to test
* @param varDepth how far "into" the environment the point must be
* @return `true`, if the point is sufficiently "deep";
* `false`, otherwise
*/
def testInteraction(pos: Vector3, varDepth: Float): Boolean
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean
def bounding: Rectangle
}
@ -32,8 +41,8 @@ trait EnvironmentCollision {
*/
final case class DeepPlane(altitude: Float)
extends EnvironmentCollision {
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
pos.z + varDepth < altitude
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
obj.Position.z + varDepth < altitude
}
def bounding: Rectangle = {
@ -55,7 +64,8 @@ final case class DeepPlane(altitude: Float)
*/
final case class DeepSquare(altitude: Float, north: Float, east: Float, south: Float, west: Float)
extends EnvironmentCollision {
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
val pos = obj.Position
pos.z + varDepth < altitude && north > pos.y && pos.y >= south && east > pos.x && pos.x >= west
}
@ -76,7 +86,8 @@ final case class DeepSquare(altitude: Float, north: Float, east: Float, south: F
*/
final case class DeepSurface(altitude: Float, north: Float, east: Float, south: Float, west: Float)
extends EnvironmentCollision {
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
val pos = obj.Position
pos.z < altitude && north > pos.y && pos.y >= south && east > pos.x && pos.x >= west
}
@ -95,7 +106,8 @@ final case class DeepCircularSurface(center: Vector3, radius: Float)
def bounding: Rectangle = Rectangle(center.y + radius, center.x + radius, center.y - radius, center.x - radius)
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
val pos = obj.Position
pos.z < center.z && Vector3.DistanceSquared(pos.xy, center.xy) < radius * radius
}
}

View file

@ -1,9 +1,7 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.environment
import enumeratum.{Enum, EnumEntry}
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.vital.Vitality
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.zones.blockmap.BlockMapEntity
import net.psforever.types.{PlanetSideGUID, Vector3}
@ -20,12 +18,12 @@ trait PieceOfEnvironment
/**
* Is the test point "within" the bounds of the represented environment?
* @param pos the test point
* @param obj entity to test
* @param varDepth how far "into" the environment the point must be
* @return `true`, if the point is sufficiently "deep";
* `false`, otherwise
*/
def testInteraction(pos: Vector3, varDepth: Float): Boolean = collision.testInteraction(pos, varDepth)
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = collision.testInteraction(obj, varDepth)
/**
* Did the test point move into or leave the bounds of the represented environment since its previous test?
@ -36,8 +34,8 @@ trait PieceOfEnvironment
* `Some(false)`, if the point has left the sufficiently "deep" region;
* `None`, otherwise
*/
def testStepIntoInteraction(pos: Vector3, previousPos: Vector3, varDepth: Float): Option[Boolean] =
PieceOfEnvironment.testStepIntoInteraction(body = this, pos, previousPos, varDepth)
def testStepIntoInteraction(pos: Vector3, obj: PlanetSideGameObject, previousPos: Vector3, varDepth: Float): Option[Boolean] =
PieceOfEnvironment.testStepIntoInteraction(body = this, obj, pos, previousPos, varDepth)
def Position: Vector3 = collision.bounding.center.asVector3 + Vector3.z(collision.altitude)
@ -52,69 +50,34 @@ trait PieceOfEnvironment
def Velocity_=(vec: Option[Vector3]): Option[Vector3] = None
}
/**
* A general description of environment and its interactive possibilities.
*/
sealed abstract class EnvironmentTrait extends EnumEntry {
def canInteractWith(obj: PlanetSideGameObject): Boolean
}
object EnvironmentAttribute extends Enum[EnvironmentTrait] {
/** glue connecting `EnumEntry` to `Enumeration` */
val values: IndexedSeq[EnvironmentTrait] = findValues
case object Water extends EnvironmentTrait {
/** water can only interact with objects that are negatively affected by being exposed to water;
* it's better this way */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj.Definition.DrownAtMaxDepth || obj.Definition.DisableAtMaxDepth || (obj match {
case p: Player => p.VehicleSeated.isEmpty
case v: Vehicle => v.MountedIn.isEmpty
case _ => true
})
}
}
case object Lava extends EnvironmentTrait {
/** lava can only interact with anything capable of registering damage */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj match {
case o: Vitality => o.Definition.Damageable
case _ => false
}
}
}
case object Death extends EnvironmentTrait {
/** death can only interact with anything capable of registering damage */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj match {
case o: Vitality => o.Definition.Damageable
case _ => false
}
}
}
case object GantryDenialField
extends EnvironmentTrait {
/** only interact with living player characters */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj match {
case p: Player => p.isAlive
case _ => false
}
}
}
case object MovementFieldTrigger
extends EnvironmentTrait {
/** only interact with living player characters or vehicles */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj match {
case p: Player => p.isAlive && p.Position != Vector3.Zero
case v: Vehicle => !v.Destroyed && v.Position != Vector3.Zero
case _ => false
}
object PieceOfEnvironment {
/**
* Did the test point move into or leave the bounds of the represented environment since its previous test?
* @param body the environment
* @param pos the test point
* @param previousPos the previous test point which is being compared against
* @param varDepth how far "into" the environment the point must be
* @return `Some(true)`, if the point has become sufficiently "deep";
* `Some(false)`, if the point has left the sufficiently "deep" region;
* `None`, if the described points only exist outside of or only exists inside of the critical region
*/
def testStepIntoInteraction(
body: PieceOfEnvironment,
obj: PlanetSideGameObject,
pos: Vector3,
previousPos: Vector3,
varDepth: Float
): Option[Boolean] = {
val originalPosition = obj.Position
obj.Position = pos
val isEncroaching = body.collision.testInteraction(obj, varDepth)
obj.Position = previousPos
val wasEncroaching = body.collision.testInteraction(obj, varDepth)
obj.Position = originalPosition
if (isEncroaching != wasEncroaching) {
Some(isEncroaching)
} else {
None
}
}
}
@ -171,34 +134,12 @@ final case class GantryDenialField(
mountPoint: Int,
collision: EnvironmentCollision
) extends PieceOfEnvironment {
def attribute = EnvironmentAttribute.GantryDenialField
def attribute: EnvironmentTrait = EnvironmentAttribute.GantryDenialField
}
final case class GeneralMovementField(
triggerAction: PlanetSideGameObject => Unit,
collision: EnvironmentCollision
) extends PieceOfEnvironment {
def attribute = EnvironmentAttribute.MovementFieldTrigger
}
object PieceOfEnvironment {
/**
* Did the test point move into or leave the bounds of the represented environment since its previous test?
* @param body the environment
* @param pos the test point
* @param previousPos the previous test point which is being compared against
* @param varDepth how far "into" the environment the point must be
* @return `Some(true)`, if the point has become sufficiently "deep";
* `Some(false)`, if the point has left the sufficiently "deep" region;
* `None`, if the described points only exist outside of or only exists inside of the critical region
*/
def testStepIntoInteraction(body: PieceOfEnvironment, pos: Vector3, previousPos: Vector3, varDepth: Float): Option[Boolean] = {
val isEncroaching = body.collision.testInteraction(pos, varDepth)
val wasEncroaching = body.collision.testInteraction(previousPos, varDepth)
if (isEncroaching != wasEncroaching) {
Some(isEncroaching)
} else {
None
}
}
def attribute: EnvironmentTrait = EnvironmentAttribute.MovementFieldTrigger
}

View file

@ -117,10 +117,9 @@ object InteractWithEnvironment {
obj: PlanetSideServerObject,
sector: SectorPopulation
): Set[PieceOfEnvironment] = {
val position = obj.Position
val depth = GlobalDefinitions.MaxDepth(obj)
sector.environmentList
.filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(position, depth))
.filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(obj, depth))
.distinctBy(_.attribute)
.toSet
}
@ -137,7 +136,7 @@ object InteractWithEnvironment {
body: PieceOfEnvironment,
obj: PlanetSideServerObject
): Option[PieceOfEnvironment] = {
if ((obj.Zone eq zone) && body.testInteraction(obj.Position, GlobalDefinitions.MaxDepth(obj))) {
if ((obj.Zone eq zone) && body.testInteraction(obj, GlobalDefinitions.MaxDepth(obj))) {
Some(body)
} else {
None

View file

@ -0,0 +1,46 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.interior
import net.psforever.objects.avatar.interaction.WithEntrance
import net.psforever.objects.serverobject.environment.interaction.InteractWithEnvironment
import net.psforever.objects.zones.InteractsWithZone
import scala.annotation.unused
trait InteriorAware {
def WhichSide: Sidedness
def WhichSide_=(@unused thisSide: Sidedness): Sidedness
}
trait TraditionalInteriorAware
extends InteriorAware {
private var side: Sidedness = Sidedness.StrictlyBetweenSides
def WhichSide: Sidedness = side
def WhichSide_=(thisSide: Sidedness): Sidedness = {
side = thisSide
WhichSide
}
}
trait InteriorAwareFromInteraction
extends InteriorAware {
awareness: InteractsWithZone =>
private lazy val withEntrance: Option[WithEntrance] = {
awareness
.interaction()
.collect { case i: InteractWithEnvironment => i.Interactions.values }
.flatten
.collectFirst { case i: WithEntrance => i }
}
def WhichSide: Sidedness = {
withEntrance.map(_.WhichSide).getOrElse(Sidedness.StrictlyBetweenSides)
}
def WhichSide_=(thisSide: Sidedness): Sidedness = {
withEntrance.foreach(_.WhichSide = thisSide)
WhichSide
}
}

View file

@ -0,0 +1,66 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.interior
import net.psforever.objects.serverobject.doors.Door
sealed trait SidenessComparison
sealed trait Sidedness {
def opposite: Sidedness
protected def value: SidenessComparison
}
object Sidedness {
sealed trait Inside
sealed trait Outside
/* Comparison values */
private case object IsInside extends SidenessComparison
private case object IsOutside extends SidenessComparison
private case object IsBetween extends SidenessComparison
/* Immutable value containers */
case object InsideOf extends Inside with Sidedness {
def opposite: Sidedness = OutsideOf
protected def value: SidenessComparison = IsInside
}
case object OutsideOf extends Outside with Sidedness {
def opposite: Sidedness = InsideOf
protected def value: SidenessComparison = IsOutside
}
case object StrictlyBetweenSides extends Inside with Outside with Sidedness {
def opposite: Sidedness = this
protected def value: SidenessComparison = IsBetween
}
/* Mutable value container */
class InBetweenSides(
private val door: Door,
private val strictly: Sidedness
) extends Inside with Outside with Sidedness {
def opposite: Sidedness = this
protected def value: SidenessComparison = {
if (door.isOpen) {
IsBetween
} else {
strictly.value
}
}
}
object InBetweenSides {
def apply(door: Door, strictly: Sidedness): InBetweenSides = new InBetweenSides(door, strictly)
}
def equals(a: Sidedness, b: Sidedness): Boolean = {
val avalue = a.value
val bvalue = b.value
(avalue eq bvalue) || avalue == IsBetween || bvalue == IsBetween
}
}

View file

@ -5,6 +5,7 @@ import akka.actor.ActorRef
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.packet.game.TriggeredSound
import net.psforever.types.Vector3
@ -23,6 +24,7 @@ class IFFLock(private val idef: IFFLockDefinition) extends Amenity with Hackable
HackSound = TriggeredSound.HackDoor
HackEffectDuration = Array(60, 180, 300, 360)
HackDuration = Array(5, 3, 1, 1)
WhichSide = Sidedness.InsideOf
/** a vector in the direction of the "outside" of a room;
* typically, any locking utility is on that same "outside"

View file

@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.mount
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
import net.psforever.objects.vital.base.DamageResolution
import net.psforever.objects.vital.etc.RadiationReason
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.resistance.StandardResistanceProfile
@ -38,17 +38,16 @@ class InteractWithRadiationCloudsSeatedInEntity(
*/
override def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
val position = target.Position
val targetList = List(target)
//collect all projectiles in sector/range
val projectiles = sector
.projectileList
.filter { cloud =>
val definition = cloud.Definition
val radius = definition.DamageRadius
definition.radiation_cloud &&
definition.AllDamageTypes.contains(DamageType.Radiation) &&
{
val radius = definition.DamageRadius
Zone.distanceCheck(target, cloud, radius * radius)
}
Zone.allOnSameSide(cloud, definition, targetList).nonEmpty &&
Zone.distanceCheck(target, cloud, radius * radius)
}
.distinct
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.pad
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.{Player, Vehicle}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.objects.serverobject.terminals.Terminal
@ -18,6 +19,8 @@ import net.psforever.types.PlanetSideGUID
* @param spDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class VehicleSpawnPad(spDef: VehicleSpawnPadDefinition) extends Amenity {
WhichSide = Sidedness.OutsideOf
def Definition: VehicleSpawnPadDefinition = spDef
}

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.pad.process
import akka.actor.Props
import akka.actor.{ActorRef, Props}
import akka.pattern.ask
import akka.util.Timeout
import net.psforever.objects.GlobalDefinitions
@ -29,11 +29,11 @@ import scala.util.Success
class VehicleSpawnControlLoadVehicle(pad: VehicleSpawnPad) extends VehicleSpawnControlBase(pad) {
def LogId = "-loader"
val railJack = context.actorOf(Props(classOf[VehicleSpawnControlRailJack], pad), s"${context.parent.path.name}-rails")
val railJack: ActorRef = context.actorOf(Props(classOf[VehicleSpawnControlRailJack], pad), s"${context.parent.path.name}-rails")
var temp: Option[VehicleSpawnControl.Order] = None
implicit val timeout = Timeout(3.seconds)
implicit val timeout: Timeout = Timeout(3.seconds)
def receive: Receive = {
case order @ VehicleSpawnControl.Order(driver, vehicle) =>
@ -42,6 +42,7 @@ class VehicleSpawnControlLoadVehicle(pad: VehicleSpawnPad) extends VehicleSpawnC
vehicle.Position = vehicle.Position - Vector3.z(
if (GlobalDefinitions.isFlightVehicle(vehicle.Definition)) 9 else 5
) //appear below the trench and doors
vehicle.WhichSide = pad.WhichSide
vehicle.Cloaked = vehicle.Definition.CanCloak && driver.Cloaked
temp = Some(order)

View file

@ -164,7 +164,7 @@ object PainboxControl {
|| amenity.Definition == GlobalDefinitions.respawn_tube
|| amenity.Definition == GlobalDefinitions.spawn_terminal
|| amenity.Definition == GlobalDefinitions.order_terminal
|| amenity.Definition == GlobalDefinitions.door)
|| amenity.isInstanceOf[Door])
&& amenity.Position.x > painbox.Position.x - planarRange && amenity.Position.x < painbox.Position.x + planarRange
&& amenity.Position.y > painbox.Position.y - planarRange && amenity.Position.y < painbox.Position.y + planarRange
&& amenity.Position.z > painbox.Position.z - belowRange && amenity.Position.z < painbox.Position.z + aboveRange

View file

@ -2,11 +2,14 @@
package net.psforever.objects.serverobject.resourcesilo
import akka.actor.{ActorContext, Props}
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.{CommonNtuContainer, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.packet.game.UseItemMessage
import net.psforever.types.Vector3
import scala.annotation.unused
class ResourceSilo extends Amenity with CommonNtuContainer {
// For the flashing red light on top of the NTU silo on.
@ -16,6 +19,7 @@ class ResourceSilo extends Amenity with CommonNtuContainer {
// For the NTU display bar
private var capacitorDisplay: Long = 0
NtuCapacitor = Definition.MaxNtuCapacitor
WhichSide = Sidedness.OutsideOf
def MaxNtuCapacitor : Float = Definition.MaxNtuCapacitor
@ -37,7 +41,7 @@ class ResourceSilo extends Amenity with CommonNtuContainer {
def Definition: ResourceSiloDefinition = GlobalDefinitions.resource_silo
def Use(player: Player, msg: UseItemMessage): ResourceSilo.Exchange = {
def Use(@unused player: Player, @unused msg: UseItemMessage): ResourceSilo.Exchange = {
ResourceSilo.ChargeEvent()
}
}

View file

@ -2,6 +2,7 @@
package net.psforever.objects.serverobject.structures
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.interior.{Sidedness, TraditionalInteriorAware}
import net.psforever.objects.vital.resistance.StandardResistanceProfile
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.resolution.DamageAndResistance
@ -25,7 +26,8 @@ abstract class Amenity
extends PlanetSideServerObject
with Vitality
with StandardResistanceProfile
with BlockMapEntity {
with BlockMapEntity
with TraditionalInteriorAware {
private[this] val log = org.log4s.getLogger("Amenity")
/** what other entity has authority over this amenity; usually either a building or a vehicle */
@ -34,6 +36,8 @@ abstract class Amenity
/** if the entity exists at a specific position relative to the owner's position */
private var offset: Option[Vector3] = None
WhichSide = Sidedness.InsideOf
def Faction: PlanetSideEmpire.Value = Owner.Faction
/**

View file

@ -2,15 +2,25 @@
package net.psforever.objects.serverobject.structures
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment}
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital._
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.objects.vital.resolution.DamageResistanceModel
import scala.annotation.unused
final case class AutoRepairStats(amount: Float, start: Long, repeat: Long, drain: Float)
trait CreateEnvironmentField {
//todo a way to probe for this property from create(...)'s output
def attribute: EnvironmentTrait
def create(@unused obj: Amenity): PieceOfEnvironment
}
abstract class AmenityDefinition(objectId: Int)
extends ObjectDefinition(objectId)
extends ObjectDefinition(objectId)
with ResistanceProfileMutators
with DamageResistanceModel
with VitalityDefinition {
@ -21,10 +31,23 @@ abstract class AmenityDefinition(objectId: Int)
var autoRepair: Option[AutoRepairStats] = None
var fields: Seq[CreateEnvironmentField] = Seq()
def autoRepair_=(auto: AutoRepairStats): Option[AutoRepairStats] = {
autoRepair = Some(auto)
autoRepair
}
def hasAutoRepair: Boolean = autoRepair.nonEmpty
def environmentField: Seq[CreateEnvironmentField] = fields
def environmentField_=(theField: CreateEnvironmentField): Seq[CreateEnvironmentField] = {
environmentField_=(Seq(theField))
}
def environmentField_=(theFields: Seq[CreateEnvironmentField]): Seq[CreateEnvironmentField] = {
fields = fields ++ theFields
environmentField
}
}

View file

@ -2,7 +2,6 @@
package net.psforever.objects.serverobject.terminals
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.equipment.EffectTarget
import scala.collection.mutable
@ -16,13 +15,12 @@ import scala.concurrent.duration.{Duration, FiniteDuration}
* between the server and client using `ProximityTerminalUseMessage` game packets.
*/
trait ProximityDefinition {
this: ObjectDefinition =>
private var interval: FiniteDuration = Duration(0, "seconds")
private var useRadius: Float = 0f //TODO belongs on a wider range of object definitions
private val targetValidation: mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject => Boolean] =
new mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject => Boolean]()
def UseRadius: Float
def Interval: FiniteDuration = interval
def Interval_=(amount: Int): FiniteDuration = {
@ -34,13 +32,6 @@ trait ProximityDefinition {
Interval
}
def UseRadius: Float = useRadius
def UseRadius_=(radius: Float): Float = {
useRadius = radius
UseRadius
}
def TargetValidation: mutable.HashMap[EffectTarget.Category.Value, PlanetSideGameObject => Boolean] = targetValidation
def Validations: Seq[PlanetSideGameObject => Boolean] = {

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.{Default, Player}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.structures.Amenity
@ -17,6 +18,8 @@ import net.psforever.services.Service
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class ProximityTerminal(tdef: ProximityTerminalDefinition) extends Terminal(tdef) with ProximityUnit {
WhichSide = Sidedness.StrictlyBetweenSides
override def Request(player: Player, msg: Any): Terminal.Exchange = {
msg match {
case message: CommonMessages.Use =>

View file

@ -2,6 +2,7 @@
package net.psforever.objects.serverobject.turret
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret
@ -14,6 +15,7 @@ class FacilityTurret(tDef: FacilityTurretDefinition)
with JammableUnit
with CaptureTerminalAware {
WeaponTurret.LoadDefinition(turret = this)
WhichSide = Sidedness.OutsideOf
def TurretOwner: SourceEntry = {
Seats
@ -36,7 +38,6 @@ class FacilityTurret(tDef: FacilityTurretDefinition)
}
object FacilityTurret {
/**
* Overloaded constructor.
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields

View file

@ -323,7 +323,7 @@ class VehicleControl(vehicle: Vehicle)
if (seatNumber == 0 && !obj.OwnerName.contains(user.Name) && obj.Definition.CanBeOwned.nonEmpty) {
//whatever vehicle was previously owned
vehicle.Zone.GUID(user.avatar.vehicle) match {
case Some(v : Vehicle) =>
case Some(v: Vehicle) =>
v.Actor ! Vehicle.Ownership(None)
case _ =>
user.avatar.vehicle = None

View file

@ -0,0 +1,50 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.vehicles.interaction
import net.psforever.objects.Vehicle
import net.psforever.objects.avatar.interaction.WithEntrance
import net.psforever.objects.serverobject.doors.InteriorDoorPassage
import net.psforever.objects.serverobject.environment.PieceOfEnvironment
import net.psforever.objects.zones.InteractsWithZone
class WithEntranceInVehicle
extends WithEntrance() {
private var warningLevel: Int = 0
private var lastWarning: Long = 0L
override def doInteractingWith(obj: InteractsWithZone, body: PieceOfEnvironment, data: Option[Any]): Unit = {
super.doInteractingWith(obj, body, data)
if (warningLevel == -1) {
warnAboutProximity(obj, msg = "@InvalidTerrain_VehicleNowSafe")
warningLevel = 0
} else if (!body.asInstanceOf[InteriorDoorPassage].door.Definition.Name.contains("garage")) {
val curr = System.currentTimeMillis()
if (curr - lastWarning >= 5000L) {
if (warningLevel > 3) {
import scala.concurrent.duration._
obj.Actor ! Vehicle.Deconstruct(Some(2.seconds))
} else if (warningLevel > 0) {
warnAboutProximity(obj, msg = "@InvalidTerrain_VehicleWillDeconstruct")
}
lastWarning = curr
warningLevel += 1
}
}
}
override def stopInteractingWith(obj: InteractsWithZone, body: PieceOfEnvironment, data: Option[Any]): Unit = {
super.stopInteractingWith(obj, body, data)
warningLevel = -1
}
private def warnAboutProximity(obj: InteractsWithZone, msg: String): Unit = {
import net.psforever.packet.game.ChatMsg
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.ChatMessageType
obj.Zone.AvatarEvents ! AvatarServiceMessage(
obj.Actor.toString(),
AvatarAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, msg))
)
}
}

View file

@ -42,6 +42,11 @@ trait DamageProperties
private var charging: Option[ChargeDamage] = None
/** a destroyed mine will detonate rather than fizzle-out */
private var sympathy: Boolean = false
/** radiation clouds create independent damage-dealing areas in a zone that last for the projectile's lifespan;
* not implicate a damage type (primary or secondary or aggravated) of `Radiation` */
private var radiationCloud: Boolean = false
/** server damage is applied when comparing against sided-ness of the source and the target */
private var damageThroughWalls: Boolean = false
def UseDamage1Subtract: Boolean = useDamage1Subtract
@ -132,4 +137,18 @@ trait DamageProperties
sympathy = chain
SympatheticExplosion
}
def radiation_cloud: Boolean = radiationCloud
def radiation_cloud_=(isCloud: Boolean): Boolean = {
radiationCloud = isCloud
radiation_cloud
}
def DamageThroughWalls: Boolean = damageThroughWalls
def DamageThroughWalls_=(through: Boolean): Boolean = {
damageThroughWalls = through
DamageThroughWalls
}
}

View file

@ -35,14 +35,19 @@ import net.psforever.actors.session.AvatarActor
import net.psforever.actors.zone.ZoneActor
import net.psforever.actors.zone.building.WarpGateLogic
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.guid.pool.NumberPool
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.environment.EnvironmentAttribute
import net.psforever.objects.serverobject.interior.{InteriorAware, Sidedness}
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vehicles.{MountedWeapons, UtilityType}
@ -50,8 +55,9 @@ import net.psforever.objects.vital.etc.ExplodingEntityReason
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.objects.vital.prop.DamageWithPosition
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.blockmap.BlockMap
import net.psforever.objects.zones.blockmap.{BlockMap, SectorPopulation}
import net.psforever.services.Service
import net.psforever.zones.Zones
import scala.annotation.tailrec
import scala.collection.mutable
@ -469,6 +475,33 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
}
}
def GetEntities(definition: ObjectDefinition): List[PlanetSideGameObject] = {
GetEntities(List(definition))
}
def GetEntities(definitions: List[ObjectDefinition]): List[PlanetSideGameObject] = {
definitions
.distinct
.groupBy(_.registerAs)
.flatMap { case (registerName, defs) =>
GetEntities(registerName)
.filter(obj => defs.contains(obj.Definition))
}
.toList
}
def GetEntities(name: String): List[PlanetSideGameObject] = {
guid
.GetPool(name)
.map { pool =>
pool
.Numbers
.flatMap(guid.apply(_))
.collect { case obj: PlanetSideGameObject => obj }
}
.getOrElse(List[PlanetSideGameObject]())
}
/**
* Wraps around the globally unique identifier system to remove an existing number pool.
* Throws exceptions for specific reasons if the pool can not be removed before the system has been started.
@ -660,17 +693,11 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
(buildings.get(building_id), guid(object_guid)) match {
case (Some(building), Some(amenity: Amenity)) =>
building.Amenities = amenity
//amenity.History(EntitySpawn(SourceEntry(amenity), this))
case (Some(_), _) | (None, _) | (_, None) => ; //let ZoneActor's sanity check catch this error
case (Some(_), _) | (None, _) | (_, None) => () //let ZoneActor's sanity check catch this error
}
})
//doors with nearby locks use those locks as their unlocking mechanism
//let ZoneActor's sanity check catch missing entities
map.doorToLock
.map { case(doorGUID: Int, lockGUID: Int) => (guid(doorGUID), guid(lockGUID)) }
.collect { case (Some(door: Door), Some(lock: IFFLock)) =>
door.Actor ! Door.UpdateMechanism(IFFLock.testLock(lock))
}
Zone.AssignOutwardSideToDoors(zone = this)
Zone.AssignSidednessToAmenities(zone = this)
//ntu management (eventually move to a generic building startup function)
buildings.values
.flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo))
@ -884,6 +911,298 @@ object Zone {
new Zone(id, map, number)
}
private def AssignOutwardSideToDoors(zone: Zone): Unit = {
//let ZoneActor's sanity check catch any missing entities
//todo there are no doors in the training zones so we may skip that
if (zone.map.cavern) {
//todo what do?
//almost all are type ancient_door and don't have many hints to determine outward-ness; there are no IFF locks
} else if (
PlanetSideEmpire.values
.filterNot(_ == PlanetSideEmpire.NEUTRAL)
.exists(fac => Zones.sanctuaryZoneNumber(fac) == zone.Number)
) {
AssignOutwardSideToSanctuaryDoors(zone)
} else {
AssignOutwardSidetoContinentDoors(zone)
}
}
private def AssignOutwardSideToSanctuaryDoors(zone: Zone): Unit = {
val map = zone.map
val guid = zone.guid
AssignOutwardsToIFFLockedDoors(zone)
//doors with IFF locks belong to towers and are always between; the locks are always outside
map.doorToLock
.map { case (door, lock) => (guid(door), guid(lock)) }
.collect { case (Some(door: Door), Some(lock: IFFLock)) =>
door.WhichSide = Sidedness.StrictlyBetweenSides
lock.WhichSide = Sidedness.OutsideOf
}
//spawn building doors
val buildings = zone.Buildings.values
val amenityList = buildings
.collect {
case b
if b.Definition.Name.startsWith("VT_building_") =>
val amenities = b.Amenities
(
amenities.collect { case door: Door if door.Definition == GlobalDefinitions.gr_door_mb_ext => door },
amenities.collect { case door: Door if door.Definition == GlobalDefinitions.gr_door_mb_lrg => door },
amenities.filter(_.Definition == GlobalDefinitions.order_terminal),
amenities.filter(_.Definition == GlobalDefinitions.respawn_tube_sanctuary)
)
}
amenityList.foreach { case (entranceDoors, trainingRangeDoors, terminals, tubes) =>
entranceDoors.foreach { door =>
val doorPosition = door.Position
val closestTerminal = terminals.minBy(t => Vector3.DistanceSquared(doorPosition, t.Position))
val closestTube = tubes.minBy(t => Vector3.DistanceSquared(doorPosition, t.Position))
door.WhichSide = Sidedness.StrictlyBetweenSides
door.Outwards = Vector3.Unit(closestTerminal.Position.xy - closestTube.Position.xy)
}
//training zone warp doors
val sampleDoor = entranceDoors.head.Position.xy;
{
val doorToDoorVector = Vector3.Unit(sampleDoor - entranceDoors(1).Position.xy)
val (listADoors, listBDoors) = trainingRangeDoors
.sortBy(door => Vector3.DistanceSquared(door.Position.xy, sampleDoor))
.partition { door =>
Vector3.ScalarProjection(doorToDoorVector, Vector3.Unit(door.Position.xy - sampleDoor)) > 0f
}
Seq(listADoors, listBDoors)
}.foreach { doors =>
val door0PosXY = doors.head.Position.xy
val door1PosXY = doors(1).Position.xy
val door2PosXY = doors(2).Position.xy
val outwardsMiddle = Vector3.Unit((door0PosXY + door2PosXY) * 0.5f - door1PosXY)
val center = door1PosXY + (outwardsMiddle * 19.5926f)
doors.head.Outwards = Vector3.Unit(center - door0PosXY)
doors(1).Outwards = outwardsMiddle
doors(2).Outwards = Vector3.Unit(center - door2PosXY)
}
}
//hart building doors
buildings
.collect {
case b
if b.Definition.Name.startsWith("orbital_building_") =>
val amenities = b.Amenities
(
amenities.filter(_.Definition == GlobalDefinitions.gr_door_mb_ext),
amenities.filter(_.Definition == GlobalDefinitions.gr_door_mb_orb)
)
}
.foreach { case (entranceDoors, hartDoors) =>
entranceDoors.foreach { door =>
val isReallyADoor = door.asInstanceOf[Door]
val doorPosition = door.Position
val closestHartDoor = hartDoors.minBy(t => Vector3.DistanceSquared(doorPosition, t.Position))
isReallyADoor.WhichSide = Sidedness.StrictlyBetweenSides
isReallyADoor.Outwards = Vector3.Unit(doorPosition.xy - closestHartDoor.Position.xy)
}
}
}
private def AssignOutwardSidetoContinentDoors(zone: Zone): Unit = {
val map = zone.map
val guid = zone.guid
AssignOutwardsToIFFLockedDoors(zone)
val buildingsToDoors = zone.Buildings.values.map(b => (b, b.Amenities.collect { case d: Door => d })).toMap
//external doors with IFF locks are always between and outside, respectively
map.doorToLock
.map { case (door, lock) => (guid(door), guid(lock)) }
.collect { case (Some(door: Door), Some(lock: IFFLock))
if door.Definition.environmentField.exists(f => f.attribute == EnvironmentAttribute.InteriorField) =>
door.WhichSide = Sidedness.StrictlyBetweenSides
lock.WhichSide = Sidedness.OutsideOf
}
//for major facilities, external doors in the courtyard are paired, connected by a passage between ground and walls
//they are the only external doors that do not have iff locks
buildingsToDoors
.filter { case (b, _) => b.BuildingType == StructureType.Facility }
.foreach { case (_, doors) =>
var unpairedDoors = doors.collect {
case d: Door
if d.Definition == GlobalDefinitions.gr_door_ext && !map.doorToLock.contains(d.GUID.guid) =>
d
}
var pairedDoors = Seq[(Door, Door)]()
while (unpairedDoors.size > 1) {
val sampleDoor = unpairedDoors.head
val sampleDoorPosition = sampleDoor.Position.xy
val distances = Float.MaxValue +: unpairedDoors
.map(d => Vector3.DistanceSquared(d.Position.xy, sampleDoorPosition))
.drop(1)
val min = distances.min
val indexOfClosestDoor = distances.indexWhere(_ == min)
val otherDoor = unpairedDoors(indexOfClosestDoor)
unpairedDoors = unpairedDoors.slice(1, indexOfClosestDoor) ++ unpairedDoors.drop(indexOfClosestDoor + 1)
pairedDoors = pairedDoors :+ (sampleDoor, otherDoor)
}
pairedDoors.foreach { case (door1, door2) =>
//give each paired courtyard door an outward-ness
val outwards = Vector3.Unit(door1.Position.xy - door2.Position.xy)
door1.Outwards = outwards
door1.WhichSide = Sidedness.StrictlyBetweenSides
door2.Outwards = Vector3.neg(outwards)
door2.WhichSide = Sidedness.StrictlyBetweenSides
}
}
//bunkers do not define a formal interior, so their doors are solely exterior
buildingsToDoors
.filter { case (b, _) => b.BuildingType == StructureType.Bunker }
.foreach { case (_, doors) =>
doors.foreach(_.WhichSide = Sidedness.OutsideOf)
}
}
private def AssignOutwardsToIFFLockedDoors(zone: Zone): Unit = {
val guid = zone.guid
//doors with nearby locks use those locks as their unlocking mechanism and their outwards indication
zone.map.doorToLock
.map { case (doorGUID: Int, lockGUID: Int) => (guid(doorGUID), guid(lockGUID)) }
.collect {
case (Some(door: Door), Some(lock: IFFLock)) =>
door.Outwards = lock.Outwards
door.Actor ! Door.UpdateMechanism(IFFLock.testLock(lock))
case _ => ()
}
}
private def AssignSidednessToAmenities(zone: Zone): Unit = {
//let ZoneActor's sanity check catch any missing entities
//todo training zones, where everything is outside
if (zone.map.cavern) {
//todo what do?
/*
quite a few amenities are disconnected from buildings
there are two orientations of terminal/spawn pad
as aforementioned, door outwards and sidedness is not assignable at the moment
*/
} else if (
PlanetSideEmpire.values
.filterNot(_ == PlanetSideEmpire.NEUTRAL)
.exists(fac => Zones.sanctuaryZoneNumber(fac) == zone.Number)
) {
AssignSidednessToSanctuaryAmenities(zone)
} else {
AssignSidednessToContinentAmenities(zone)
}
}
private def AssignSidednessToSanctuaryAmenities(zone: Zone): Unit = {
val map = zone.map
val guid = zone.guid
//only tower doors possess locks and those are always external
map.doorToLock
.map { case (_, lock) => guid(lock) }
.collect {
case Some(lock: IFFLock) =>
lock.WhichSide = Sidedness.OutsideOf
}
//medical terminals are always inside
zone.buildings
.values
.flatMap(_.Amenities)
.collect {
case pt: ProximityTerminal if pt.Definition == GlobalDefinitions.medical_terminal =>
pt.WhichSide = Sidedness.InsideOf
}
//repair silos and landing pads have multiple components and all of these are outside
//we have to search all terminal entities because the repair silos are not installed anywhere
guid
.GetPool(name = "terminals")
.map(_.Numbers.flatMap(number => guid(number)))
.getOrElse(List())
.collect {
case pt: ProximityTerminal
if pt.Definition == GlobalDefinitions.repair_silo =>
val guid = pt.GUID.guid
Seq(guid, guid + 1, guid + 2, guid + 3)
case pt: ProximityTerminal
if pt.Definition.Name.startsWith("pad_landing_") =>
val guid = pt.GUID.guid
Seq(guid, guid + 1, guid + 2)
}
.flatten[Int]
.map(guid(_))
.collect {
case Some(pt: ProximityTerminal) =>
pt.WhichSide = Sidedness.OutsideOf
}
//the following terminals are installed outside
map.terminalToSpawnPad
.keys
.flatMap(guid(_))
.collect {
case terminal: Terminal =>
terminal.WhichSide = Sidedness.OutsideOf
}
}
private def AssignSidednessToContinentAmenities(zone: Zone): Unit = {
val map = zone.map
val guid = zone.guid
val buildingsMap = zone.buildings.values
//door locks on external doors are also external while the door is merely "between"; all other locks are internal
map.doorToLock
.map { case (door, lock) => (guid(door), guid(lock))}
.collect {
case (Some(door: Door), Some(lock: IFFLock))
if door.Definition.environmentField.exists(f => f.attribute == EnvironmentAttribute.InteriorField) =>
lock.WhichSide = Sidedness.OutsideOf
}
//medical terminals are always inside
buildingsMap
.flatMap(_.Amenities)
.collect {
case pt: ProximityTerminal
if pt.Definition == GlobalDefinitions.medical_terminal || pt.Definition == GlobalDefinitions.adv_med_terminal =>
pt.WhichSide = Sidedness.InsideOf
}
//repair silos and landing pads have multiple components and all of these are outside
buildingsMap
.flatMap(_.Amenities)
.collect {
case pt: ProximityTerminal
if pt.Definition == GlobalDefinitions.repair_silo =>
val guid = pt.GUID.guid
Seq(guid, guid + 1, guid + 2, guid + 3)
case pt: ProximityTerminal
if pt.Definition.Name.startsWith("pad_landing_") =>
val guid = pt.GUID.guid
Seq(guid, guid + 1, guid + 2)
}
.toSeq
.flatten[Int]
.map(guid(_))
.collect {
case Some(pt: ProximityTerminal) =>
pt.WhichSide = Sidedness.OutsideOf
}
//all vehicle spawn pads are outside, save for the ground vehicle pad in the tech plants
buildingsMap.collect {
case b
if b.Definition == GlobalDefinitions.tech_plant =>
b.Amenities
.collect { case pad: VehicleSpawnPad => pad }
.minBy(_.Position.z)
.WhichSide = Sidedness.InsideOf
}
//all vehicle terminals are outside of their owning facilities in the courtyard
//the only exceptions are vehicle terminals in tech plants and the dropship center air terminal
map.terminalToSpawnPad
.keys
.flatMap(guid(_))
.collect {
case terminal: Terminal
if terminal.Definition != GlobalDefinitions.dropship_vehicle_terminal &&
terminal.Owner.asInstanceOf[Building].Definition != GlobalDefinitions.tech_plant =>
terminal.WhichSide = Sidedness.OutsideOf
}
}
object Population {
/**
@ -1329,42 +1648,96 @@ object Zone {
}
/**
* na
* @see `DamageWithPosition`
* @see `Zone.blockMap.sector`
* @param zone the zone in which the explosion should occur
* @param source a game entity that is treated as the origin and is excluded from results
* @param damagePropertiesBySource information about the effect/damage
* @return a list of affected entities
*/
def findAllTargets(
zone: Zone,
source: PlanetSideGameObject with Vitality,
damagePropertiesBySource: DamageWithPosition
): List[PlanetSideServerObject with Vitality] = {
findAllTargets(zone, source.Position, damagePropertiesBySource).filter { target => target ne source }
* na
* @param source na
* @param damageProperties na
* @param targets na
* @return na
*/
def allOnSameSide(
source: PlanetSideGameObject,
damageProperties: DamageWithPosition,
targets: List[PlanetSideServerObject with Vitality]
): List[PlanetSideServerObject with Vitality] = {
source match {
case awareSource: InteriorAware if !damageProperties.DamageThroughWalls =>
allOnSameSide(awareSource.WhichSide, targets)
case _ if !damageProperties.DamageThroughWalls =>
val sourcePosition = source.Position
targets
.sortBy(t => Vector3.DistanceSquared(sourcePosition, t.Position))
.collectFirst { case awareSource: InteriorAware => allOnSameSide(awareSource.WhichSide, targets) }
.getOrElse(targets)
case _ =>
targets
}
}
/**
* na
* @param side na
* @param targets na
* @return na
*/
def allOnSameSide(
side: Sidedness,
targets: List[PlanetSideServerObject with Vitality]
): List[PlanetSideServerObject with Vitality] = {
targets.flatMap {
case awareTarget: InteriorAware if !Sidedness.equals(side, awareTarget.WhichSide) => None
case anyTarget => Some(anyTarget)
}
}
/**
* na
* @see `DamageWithPosition`
* @see `Zone.blockMap.sector`
* @param sourcePosition a custom position that is used as the origin of the explosion;
* not necessarily related to source
* @param zone the zone in which the explosion should occur
* @param source a game entity that is treated as the origin and is excluded from results
* @param damagePropertiesBySource information about the effect/damage
* @return a list of affected entities
*/
def findAllTargets(
sourcePosition: Vector3
)
(
zone: Zone,
source: PlanetSideGameObject with Vitality,
damagePropertiesBySource: DamageWithPosition
): List[PlanetSideServerObject with Vitality] = {
findAllTargets(zone, sourcePosition, damagePropertiesBySource).filter { target => target ne source }
findAllTargets(
zone,
source,
source.Position,
damagePropertiesBySource,
damagePropertiesBySource.DamageRadius,
getAllTargets
)
}
/**
* na
* @see `DamageWithPosition`
* @see `Zone.blockMap.sector`
* @param zone the zone in which the explosion should occur
* @param sourcePosition a custom position that is used as the origin of the explosion;
* not necessarily related to source
* @param source a game entity that is treated as the origin and is excluded from results
* @param damagePropertiesBySource information about the effect/damage
* @return a list of affected entities
*/
def findAllTargets(
zone: Zone,
source: PlanetSideGameObject with Vitality,
sourcePosition: Vector3,
damagePropertiesBySource: DamageWithPosition
): List[PlanetSideServerObject with Vitality] = {
findAllTargets(
zone,
source,
sourcePosition,
damagePropertiesBySource,
damagePropertiesBySource.DamageRadius,
getAllTargets
)
}
/**
@ -1374,24 +1747,66 @@ object Zone {
* @param zone the zone in which the explosion should occur
* @param sourcePosition a position that is used as the origin of the explosion
* @param damagePropertiesBySource information about the effect/damage
* @param getTargetsFromSector get this list of entities from a sector
* @return a list of affected entities
*/
def findAllTargets(
zone: Zone,
source: PlanetSideGameObject with Vitality,
sourcePosition: Vector3,
damagePropertiesBySource: DamageWithPosition
damagePropertiesBySource: DamageWithPosition,
radius: Float,
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
): List[PlanetSideServerObject with Vitality] = {
val sourcePositionXY = sourcePosition.xy
val sectors = zone.blockMap.sector(sourcePositionXY, damagePropertiesBySource.DamageRadius)
allOnSameSide(
source,
damagePropertiesBySource,
findAllTargets(zone, sourcePosition, radius, getTargetsFromSector).filter { target => target ne source }
)
}
def findAllTargets(
sector: SectorPopulation,
source: PlanetSideGameObject with Vitality,
damagePropertiesBySource: DamageWithPosition,
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
): List[PlanetSideServerObject with Vitality] = {
allOnSameSide(
source,
damagePropertiesBySource,
getTargetsFromSector(sector)
)
}
/**
* na
* @see `DamageWithPosition`
* @see `Zone.blockMap.sector`
* @param zone the zone in which the explosion should occur
* @param sourcePosition a position that is used as the origin of the explosion
* @param radius idistance
* @param getTargetsFromSector get this list of entities from a sector
* @return a list of affected entities
*/
def findAllTargets(
zone: Zone,
sourcePosition: Vector3,
radius: Float,
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
): List[PlanetSideServerObject with Vitality] = {
getTargetsFromSector(zone.blockMap.sector(sourcePosition.xy, radius))
}
def getAllTargets(sector: SectorPopulation): List[PlanetSideServerObject with Vitality] = {
//collect all targets that can be damaged
//players
val playerTargets = sectors.livePlayerList.filterNot { _.VehicleSeated.nonEmpty }
val playerTargets = sector.livePlayerList.filterNot { _.VehicleSeated.nonEmpty }
//vehicles
val vehicleTargets = sectors.vehicleList.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
val vehicleTargets = sector.vehicleList.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
//deployables
val deployableTargets = sectors.deployableList.filterNot { _.Destroyed }
val deployableTargets = sector.deployableList.filterNot { _.Destroyed }
//amenities
val soiTargets = sectors.amenityList.collect { case amenity: Vitality if !amenity.Destroyed => amenity }
val soiTargets = sector.amenityList.collect { case amenity: Vitality if !amenity.Destroyed => amenity }
//altogether ...
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
}
@ -1411,7 +1826,9 @@ object Zone {
target: PlanetSideGameObject with FactionAffinity with Vitality
): DamageInteraction = {
explosionDamage(instigation, target.Position)(source, target)
}/**
}
/**
* na
* @param instigation what previous event happened, if any, that caused this explosion
* @param explosionPosition the coordinates of the detected explosion

View file

@ -200,7 +200,11 @@ class Sector(val longitude: Int, val latitude: Int, val span: Int)
case b: Building =>
buildings.list.size < buildings.addTo(b).size
case a: Amenity =>
amenities.list.size < amenities.addTo(a).size
val added = amenities.list.size < amenities.addTo(a).size
if (added) {
a.Definition.environmentField.foreach(field => environment.addTo(field.create(a)))
}
added
case e: PieceOfEnvironment =>
environment.list.size < environment.addTo(e).size
case p: Projectile =>

View file

@ -1,10 +1,12 @@
// Copyright (c) 2021 PSForever
package net.psforever.packet.game
import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.{PlanetSideGUID, Vector3}
import scodec.Attempt.Successful
import scodec.Codec
import scodec.bits.BitVector
import scodec.{Attempt, Codec}
import scodec.codecs._
import shapeless.{::, HNil}
@ -15,7 +17,7 @@ object TerrainCondition extends Enumeration {
type Type = Value
val Safe, Unsafe = Value
implicit val codec = PacketHelpers.createEnumerationCodec(e = this, uint(bits = 1))
implicit val codec: Codec[TerrainCondition.Value] = PacketHelpers.createEnumerationCodec(e = this, uint(bits = 1))
}
/**
@ -34,8 +36,8 @@ final case class InvalidTerrainMessage(
pos: Vector3
) extends PlanetSideGamePacket {
type Packet = InvalidTerrainMessage
def opcode = GamePacketOpcode.InvalidTerrainMessage
def encode = InvalidTerrainMessage.encode(this)
def opcode: Type = GamePacketOpcode.InvalidTerrainMessage
def encode: Attempt[BitVector] = InvalidTerrainMessage.encode(this)
}
object InvalidTerrainMessage extends Marshallable[InvalidTerrainMessage] {

View file

@ -247,7 +247,7 @@ object ChatMessageType extends Enum[ChatMessageType] {
case object CMT_ADD_VANUMODULE extends ChatMessageType // /moduleadd
case object CMT_REMOVE_VANUMODULE extends ChatMessageType // /moduleremove OR /modulerm
case object CMT_DEBUG_MASSIVE extends ChatMessageType // /debugmassive
case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // ???
case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // The "Next" button on the /debugmassive UI produces this message.
case object UNK_222 extends ChatMessageType // "CTF Flag stolen"
// Plays a trumpet-like sound and displays the message: "<Base Type> <Base Name> has spawned a LLU"
// "It must be taken to <Base Type> <Base Name>'s Control Console within 15 minutes or the hack will fail!"

View file

@ -101,6 +101,8 @@ final case class Vector3(x: Float, y: Float, z: Float) {
object Vector3 {
final val Zero: Vector3 = Vector3(0f, 0f, 0f)
def unapply(v: Vector3): Option[(Float, Float, Float)] = Some((v.x, v.y, v.z))
private def closeToInsignificance(d: Float, epsilon: Float = 10f): Float = {
val ulp = math.ulp(epsilon)
math.signum(d) match {

View file

@ -182,6 +182,7 @@ case class HartConfig(
case class DevelopmentConfig(
unprivilegedGmCommands: Seq[ChatMessageType],
unprivilegedGmBangCommands: Seq[String],
netSim: NetSimConfig
)

View file

@ -177,23 +177,44 @@ object Zones {
)
private val doorTypes = Seq(
"gr_door_garage_int",
"gr_door_int",
"gr_door_med",
"spawn_tube_door",
"amp_cap_door",
"ancient_door",
"ancient_garage_door",
"cryo_med_door",
"cryo_room_door",
"door",
"door_airlock",
"door_airlock_orb",
"door_dsp",
"door_garage",
"door_interior",
"door_mb",
"door_mb_garage",
"door_mb_main",
"door_mb_orb",
"door_mb_side",
"door_nc_garage",
"door_nc_rotating",
"door_ncside",
"door_orbspawn",
"door_spawn_mb",
"garage_door",
"gr_door_airlock",
"gr_door_ext",
"gr_door_garage_ext",
"gr_door_garage_int",
"gr_door_int",
"gr_door_main",
"gr_door_mb_ext",
"gr_door_mb_int",
"gr_door_mb_lrg",
"gr_door_mb_obsd",
"gr_door_mb_orb",
"door_spawn_mb",
"ancient_door",
"ancient_garage_door"
"gr_door_med",
"main_door",
"shield_door",
"spawn_tube_door",
"spawn_tube_door_coffin"
)
lazy val zoneMaps: Seq[ZoneMap] = {
@ -384,14 +405,6 @@ object Zones {
owningBuildingGuid = ownerGuid
)
case "gr_door_mb_orb" =>
zoneMap
.addLocalObject(
obj.guid,
Door.Constructor(obj.position, GlobalDefinitions.gr_door_mb_orb),
owningBuildingGuid = ownerGuid
)
case doorType if doorType.equals("door_spawn_mb") || doorType.equals("spawn_tube_door") =>
zoneMap.addLocalObject(
obj.guid,
@ -404,23 +417,29 @@ object Zones {
case objectType if doorTypes.contains(objectType) =>
zoneMap
.addLocalObject(obj.guid, Door.Constructor(obj.position), owningBuildingGuid = ownerGuid)
.addLocalObject(
obj.guid,
Door.Constructor(
obj.position,
DefinitionUtil.fromString(objectType).asInstanceOf[DoorDefinition]
),
owningBuildingGuid = ownerGuid
)
case "locker_cryo" | "locker_med" | "mb_locker" =>
zoneMap
.addLocalObject(obj.guid, Locker.Constructor(obj.position), owningBuildingGuid = ownerGuid)
case "lock_external" | "lock_garage" | "lock_small" =>
val closestDoor = doors.minBy(d => Vector3.Distance(d.position, obj.position))
// Since tech plant garage locks are the only type where the lock does not face the same direction as the door we need to apply an offset for those, otherwise the door won't operate properly when checking inside/outside angles.
val yawOffset = if (obj.objectType == "lock_garage") 90 else 0
val position = obj.position
val closestDoor = doors.minBy(d => Vector3.Distance(d.position, position))
// Ignore duplicate lock objects, for example Sobek (and other Dropship Centers) CC door has 2 locks stacked on top of each other
if (!zoneMap.doorToLock.keys.iterator.contains(closestDoor.guid)) {
// Since tech plant garage locks are the only type where the lock does not face the same direction as the door we need to apply an offset for those, otherwise the door won't operate properly when checking inside/outside angles.
val yawOffset = if (obj.objectType == "lock_garage") 90 else 0
zoneMap.addLocalObject(
obj.guid,
IFFLock.Constructor(obj.position, Vector3(0, 0, obj.yaw + yawOffset)),
IFFLock.Constructor(position, Vector3.z(obj.yaw + yawOffset)),
owningBuildingGuid = ownerGuid,
doorGuid = closestDoor.guid
)
@ -798,7 +817,7 @@ object Zones {
}
}
lazy val cavernLattice = {
lazy val cavernLattice: Map[String, Iterable[Iterable[String]]] = {
val res = Source.fromResource(s"zonemaps/lattice.json")
val json = res.mkString
res.close()

View file

@ -2,8 +2,8 @@
package objects
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.{GlobalDefinitions, Player, Tool, Vehicle}
import net.psforever.objects.definition.VehicleDefinition
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player, Tool, Vehicle}
import net.psforever.objects.definition.{ObjectDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.environment._
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalDefinition}
import net.psforever.objects.vital.Vitality
@ -11,6 +11,10 @@ import net.psforever.packet.game.objectcreate.ObjectClass
import net.psforever.types._
import org.specs2.mutable.Specification
private class PSGOTest() extends PlanetSideGameObject() {
override def Definition: ObjectDefinition = null
}
class EnvironmentCollisionTest extends Specification {
"DeepPlane" should {
val point: Float = 10f
@ -21,15 +25,23 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
plane.testInteraction(Vector3(0,0,10), varDepth = -1) mustEqual true
plane.testInteraction(Vector3(0,0, 9), varDepth = 0) mustEqual true
plane.testInteraction(Vector3(0,0, 8), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(0,0,9)
plane.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(0,0,8)
plane.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
plane.testInteraction(Vector3(0,0,11), varDepth = -1) mustEqual false
plane.testInteraction(Vector3(0,0,10), varDepth = 0) mustEqual false
plane.testInteraction(Vector3(0,0, 9), varDepth = 1) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(0,0,11)
plane.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,0,9)
plane.testInteraction(obj, varDepth = 1) mustEqual false
}
}
@ -42,23 +54,39 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
square.testInteraction(Vector3(1,1, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(1,8, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(8,8, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(8,1, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(1,1,10), varDepth = -1) mustEqual true
square.testInteraction(Vector3(1,1, 9), varDepth = 0) mustEqual true
square.testInteraction(Vector3(1,1, 8), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(1,1, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,8, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,8, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,1, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1,10)
square.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(1,1, 9)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1, 8)
square.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
square.testInteraction(Vector3(1,0, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(1,9, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(0,9, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(0,1, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(1,1,11), varDepth = -1) mustEqual false
square.testInteraction(Vector3(1,1,10), varDepth = 0) mustEqual false
square.testInteraction(Vector3(1,1, 9), varDepth = 1) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(1,0, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,9, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,9, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,1, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,1,11)
square.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(1,1,10)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,1, 9)
square.testInteraction(obj, varDepth = 1) mustEqual false
}
}
@ -71,22 +99,37 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
surface.testInteraction(Vector3(1,1,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,8,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(8,8,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(8,1,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,1,9), varDepth = -1) mustEqual true
surface.testInteraction(Vector3(1,1,9), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,1,9), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(1,1,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,8,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,8,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,1,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1,9)
surface.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(1,1,9)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1,9)
surface.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
surface.testInteraction(Vector3(1,0, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(1,9, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(0,9, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(0,1, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(1,1,11), varDepth = -1) mustEqual false
surface.testInteraction(Vector3(1,1,10), varDepth = 0) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(1,0, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,9, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,9, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,1, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,1,11)
surface.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(1,1,10)
surface.testInteraction(obj, varDepth = 0) mustEqual false
}
}
@ -100,22 +143,37 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
surface.testInteraction(Vector3(3,1,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,3,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(3,5,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(5,3,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(2,2,9), varDepth = -1) mustEqual true
surface.testInteraction(Vector3(2,2,9), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(2,2,9), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(3,1,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,3,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(3,5,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(5,3,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(2,2,9)
surface.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(2,2,9)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(2,2,9)
surface.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
surface.testInteraction(Vector3(3,0, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(0,3, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(3,6, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(6,3, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(2,2,11), varDepth = -1) mustEqual false
surface.testInteraction(Vector3(2,2,10), varDepth = 0) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(3,0, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,3, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(3,6, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(6,3, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(2,2,11)
surface.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(2,2,10)
surface.testInteraction(obj, varDepth = 0) mustEqual false
}
}
}
@ -237,21 +295,23 @@ class SeaLevelTest extends Specification {
}
"must have interaction that passes (same as DeepPlane)" in {
plane.testInteraction(Vector3(0,0,10), varDepth = -1) mustEqual
level.testInteraction(Vector3(0,0,10), varDepth = -1)
plane.testInteraction(Vector3(0,0, 9), varDepth = 0) mustEqual
level.testInteraction(Vector3(0,0, 9), varDepth = 0)
plane.testInteraction(Vector3(0,0, 8), varDepth = 1) mustEqual
level.testInteraction(Vector3(0,0, 8), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = -1) mustEqual level.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(0,0, 9)
plane.testInteraction(obj, varDepth = 0) mustEqual level.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,0, 8)
plane.testInteraction(obj, varDepth = 1) mustEqual level.testInteraction(obj, varDepth = 1)
}
"must have interaction that fails (same as DeepPlane)" in {
plane.testInteraction(Vector3(0,0,11), varDepth = -1) mustEqual
level.testInteraction(Vector3(0,0,11), varDepth = -1)
plane.testInteraction(Vector3(0,0,10), varDepth = 0) mustEqual
level.testInteraction(Vector3(0,0,10), varDepth = 0)
plane.testInteraction(Vector3(0,0, 9), varDepth = 1) mustEqual
level.testInteraction(Vector3(0,0, 9), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(0,0,11)
plane.testInteraction(obj, varDepth = -1) mustEqual level.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = 0) mustEqual level.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,0, 9)
plane.testInteraction(obj, varDepth = 1) mustEqual level.testInteraction(obj, varDepth = 1)
}
}
}
@ -267,43 +327,45 @@ class PoolTest extends Specification {
}
"must have interaction that passes (same as DeepSquare)" in {
pool.testInteraction(Vector3(1,1, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,1, 0), varDepth = 0)
pool.testInteraction(Vector3(1,8, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,8, 0), varDepth = 0)
pool.testInteraction(Vector3(8,8, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(8,8, 0), varDepth = 0)
pool.testInteraction(Vector3(8,1, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(8,1, 0), varDepth = 0)
pool.testInteraction(Vector3(1,1,10), varDepth = -1) mustEqual
square.testInteraction(Vector3(1,1,10), varDepth = -1)
pool.testInteraction(Vector3(1,1, 9), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,1, 9), varDepth = 0)
pool.testInteraction(Vector3(1,1, 8), varDepth = 1) mustEqual
square.testInteraction(Vector3(1,1, 8), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(1,1, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,8, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(8,8, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(8,1, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1,10)
pool.testInteraction(obj, varDepth = -1) mustEqual square.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(1,1, 9)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1, 8)
pool.testInteraction(obj, varDepth = 1) mustEqual square.testInteraction(obj, varDepth = 1)
}
"must have interaction that fails (same as DeepSquare)" in {
pool.testInteraction(Vector3(1,0, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,0, 0), varDepth = 0)
pool.testInteraction(Vector3(1,9, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,9, 0), varDepth = 0)
pool.testInteraction(Vector3(0,9, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(0,9, 0), varDepth = 0)
pool.testInteraction(Vector3(0,1, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(0,1, 0), varDepth = 0)
pool.testInteraction(Vector3(1,1,11), varDepth = -1) mustEqual
square.testInteraction(Vector3(1,1,11), varDepth = -1)
pool.testInteraction(Vector3(1,1,10), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,1,10), varDepth = 0)
pool.testInteraction(Vector3(1,1, 9), varDepth = 1) mustEqual
square.testInteraction(Vector3(1,1, 9), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(1,0, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,9, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,9, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,1, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1,11)
pool.testInteraction(obj, varDepth = -1) mustEqual square.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(1,1,10)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1, 9)
pool.testInteraction(obj, varDepth = 1) mustEqual square.testInteraction(obj, varDepth = 1)
}
}
}
class GantryDenialField extends Specification {
val square = DeepSquare(0, 1, 10, 10, 1)
val square: DeepSquare = DeepSquare(0, 1, 10, 10, 1)
"GantryDenialField" should {
"always has the environmental attribute of 'GantryDenialField'" in {
@ -319,19 +381,19 @@ class PieceOfEnvironmentTest extends Specification {
val level = SeaLevel(10f)
"detect entering a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,9), Vector3(0,0,11), varDepth = 0).contains(true) mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,9), Vector3(0,0,11), varDepth = 0).contains(true) mustEqual true
}
"detect leaving a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,11), Vector3(0,0,9), varDepth = 0).contains(false) mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,11), Vector3(0,0,9), varDepth = 0).contains(false) mustEqual true
}
"not detect moving outside of a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,12), Vector3(0,0,11), varDepth = 0).isEmpty mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,12), Vector3(0,0,11), varDepth = 0).isEmpty mustEqual true
}
"not detect moving within a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,9), Vector3(0,0,8), varDepth = 0).isEmpty mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,9), Vector3(0,0,8), varDepth = 0).isEmpty mustEqual true
}
}
}