mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
cud emp and os
This commit is contained in:
parent
7d8dd52259
commit
3f7f6ee4c0
|
|
@ -23,7 +23,7 @@ add_property boomer_trigger equiptime 500
|
||||||
add_property chainblade equiptime 250
|
add_property chainblade equiptime 250
|
||||||
add_property chainblade holstertime 250
|
add_property chainblade holstertime 250
|
||||||
add_property colossus_flight requirement_award0 false
|
add_property colossus_flight requirement_award0 false
|
||||||
add_property command_detonater allowed false
|
add_property command_detonater allowed true
|
||||||
add_property command_detonater equiptime 500
|
add_property command_detonater equiptime 500
|
||||||
add_property command_detonater holstertime 500
|
add_property command_detonater holstertime 500
|
||||||
add_property cycler equiptime 600
|
add_property cycler equiptime 600
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,10 @@ object AvatarActor {
|
||||||
final case class UpdatePurchaseTime(definition: BasicDefinition, time: LocalDateTime = LocalDateTime.now())
|
final case class UpdatePurchaseTime(definition: BasicDefinition, time: LocalDateTime = LocalDateTime.now())
|
||||||
extends Command
|
extends Command
|
||||||
|
|
||||||
|
/** rchase time for the use of calculating cooldowns */
|
||||||
|
final case class UpdateCUDTime(action: String, time: LocalDateTime = LocalDateTime.now())
|
||||||
|
extends Command
|
||||||
|
|
||||||
/** Set use time for the use of calculating cooldowns */
|
/** Set use time for the use of calculating cooldowns */
|
||||||
final case class UpdateUseTime(definition: BasicDefinition, time: LocalDateTime = LocalDateTime.now()) extends Command
|
final case class UpdateUseTime(definition: BasicDefinition, time: LocalDateTime = LocalDateTime.now()) extends Command
|
||||||
|
|
||||||
|
|
@ -459,7 +463,15 @@ object AvatarActor {
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case _: Exception => ()
|
case _: Exception =>
|
||||||
|
val cooldown = LocalDateTime.parse(b)
|
||||||
|
name match {
|
||||||
|
case "orbital_strike" if now.compareTo(cooldown.plusMillis(3.hours.toMillis.toInt)) == -1 =>
|
||||||
|
cooldowns.put(name, cooldown)
|
||||||
|
case "emp_blast" | "reveal_friendlies" | "reveal_enemies" if now.compareTo(cooldown.plusMillis(20.minutes.toMillis.toInt)) == -1 =>
|
||||||
|
cooldowns.put(name, cooldown)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"ignoring invalid cooldown string: '$value'")
|
log.warn(s"ignoring invalid cooldown string: '$value'")
|
||||||
|
|
@ -1549,6 +1561,21 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
|
case UpdateCUDTime(action, time) =>
|
||||||
|
var theTimes = avatar.cooldowns.purchase
|
||||||
|
var updateTheTimes: Boolean = false
|
||||||
|
Avatar.cudCooldowns.get(action) match {
|
||||||
|
case Some(_) =>
|
||||||
|
//only send for items with cooldowns
|
||||||
|
updateTheTimes = true
|
||||||
|
theTimes = theTimes.updated(action, time)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
if (updateTheTimes) {
|
||||||
|
avatarCopy(avatar.copy(cooldowns = avatar.cooldowns.copy(purchase = theTimes)))
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case UpdateUseTime(definition, time) =>
|
case UpdateUseTime(definition, time) =>
|
||||||
if (!Avatar.useCooldowns.contains(definition)) {
|
if (!Avatar.useCooldowns.contains(definition)) {
|
||||||
log.warn(s"${avatar.name} is updating a use time for item '${definition.Name}' that has no cooldown")
|
log.warn(s"${avatar.name} is updating a use time for item '${definition.Name}' that has no cooldown")
|
||||||
|
|
@ -2655,6 +2682,43 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
if (keysToDrop.nonEmpty) {
|
if (keysToDrop.nonEmpty) {
|
||||||
val cdown = avatar.cooldowns
|
val cdown = avatar.cooldowns
|
||||||
|
val cud = Array("orbital_strike", "emp_blast", "reveal_friendlies", "reveal_enemies")
|
||||||
|
keysToDrop.foreach { key =>
|
||||||
|
if (cud.contains(key)) {
|
||||||
|
avatar
|
||||||
|
.cooldowns
|
||||||
|
.purchase
|
||||||
|
.find { case (name, _) => name.equals(key) }
|
||||||
|
.flatMap { case (name, purchaseTime) =>
|
||||||
|
val secondsSincePurchase = Seconds.secondsBetween(purchaseTime, LocalDateTime.now()).getSeconds
|
||||||
|
Avatar
|
||||||
|
.cudCooldowns
|
||||||
|
.find(_._1.equals(name))
|
||||||
|
.collect {
|
||||||
|
case (action, cooldown) =>
|
||||||
|
(action, cooldown.toSeconds - secondsSincePurchase)
|
||||||
|
}
|
||||||
|
.orElse {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.collect {
|
||||||
|
case (action, remainingTime) if remainingTime > 0 =>
|
||||||
|
val convertTime = remainingTime * 1000
|
||||||
|
keysToDrop = keysToDrop.filterNot(_ == action)
|
||||||
|
action match {
|
||||||
|
case "orbital_strike" =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 60, convertTime))
|
||||||
|
case "emp_blast" =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 59, convertTime))
|
||||||
|
case "reveal_enemies" =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 58, convertTime))
|
||||||
|
case "reveal_friendlies" =>
|
||||||
|
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 57, convertTime))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
avatarCopy(avatar.copy(cooldowns = cdown.copy(purchase = cdown.purchase.removedAll(keysToDrop))))
|
avatarCopy(avatar.copy(cooldowns = cdown.copy(purchase = cdown.purchase.removedAll(keysToDrop))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -517,7 +517,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
case packet: WeaponLazeTargetPositionMessage =>
|
case packet: WeaponLazeTargetPositionMessage =>
|
||||||
logic.shooting.handleWeaponLazeTargetPosition(packet)
|
logic.shooting.handleWeaponLazeTargetPosition(packet)
|
||||||
|
|
||||||
case _: UplinkRequest => ()
|
case packet: UplinkRequest =>
|
||||||
|
logic.shooting.handleUplinkRequest(packet)
|
||||||
|
|
||||||
case packet: HitMessage =>
|
case packet: HitMessage =>
|
||||||
logic.shooting.handleDirectHit(packet)
|
logic.shooting.handleDirectHit(packet)
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec
|
||||||
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, Player, SpecialEmp, Tool, Vehicle}
|
import net.psforever.objects.{BoomerDeployable, BoomerTrigger, Player, SpecialEmp, Tool, Vehicle}
|
||||||
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
|
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
|
||||||
import net.psforever.objects.zones.{Zone, ZoneProjectile}
|
import net.psforever.objects.zones.{Zone, ZoneProjectile}
|
||||||
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, ProjectileStateMessage, ReloadMessage, SplashHitMessage, UplinkRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
import net.psforever.packet.game.{AIDamage, AvatarGrenadeStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, HitMessage, LashMessage, LongRangeProjectileInfoMessage, OrbitalStrikeWaypointMessage, ProjectileStateMessage, ReloadMessage, SplashHitMessage, TriggerEffectMessage, TriggeredEffectLocation, UplinkRequest, UplinkRequestType, UplinkResponse, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage}
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.{ValidPlanetSideGUID, Vector3}
|
||||||
|
|
||||||
object WeaponAndProjectileLogic {
|
object WeaponAndProjectileLogic {
|
||||||
def apply(ops: WeaponAndProjectileOperations): WeaponAndProjectileLogic = {
|
def apply(ops: WeaponAndProjectileOperations): WeaponAndProjectileLogic = {
|
||||||
|
|
@ -50,7 +50,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleUplinkRequest(packet: UplinkRequest): Unit = {
|
def handleUplinkRequest(packet: UplinkRequest): Unit = {
|
||||||
sessionLogic.administrativeKick(player)
|
ops.handleUplinkRequest(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleAvatarGrenadeState(pkt: AvatarGrenadeStateMessage): Unit = {
|
def handleAvatarGrenadeState(pkt: AvatarGrenadeStateMessage): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
|
||||||
|
|
||||||
def handleWeaponLazeTargetPosition(pkt: WeaponLazeTargetPositionMessage): Unit = { /* intentionally blank */ }
|
def handleWeaponLazeTargetPosition(pkt: WeaponLazeTargetPositionMessage): Unit = { /* intentionally blank */ }
|
||||||
|
|
||||||
def handleUplinkRequest(packet: UplinkRequest): Unit = {
|
def handleUplinkRequest(packet: UplinkRequest): Unit = { /* intentionally blank */ }
|
||||||
val UplinkRequest(code, _, _) = packet
|
|
||||||
val playerFaction = player.Faction
|
|
||||||
//todo this is not correct
|
|
||||||
code match {
|
|
||||||
case UplinkRequestType.RevealFriendlies =>
|
|
||||||
sendResponse(UplinkResponse(code.value, continent.LivePlayers.count(_.Faction == playerFaction)))
|
|
||||||
case UplinkRequestType.RevealEnemies =>
|
|
||||||
sendResponse(UplinkResponse(code.value, continent.LivePlayers.count(_.Faction != playerFaction)))
|
|
||||||
case _ => ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def handleAvatarGrenadeState(pkt: AvatarGrenadeStateMessage): Unit = { /* intentionally blank */ }
|
def handleAvatarGrenadeState(pkt: AvatarGrenadeStateMessage): Unit = { /* intentionally blank */ }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package net.psforever.actors.session.support
|
||||||
|
|
||||||
import akka.actor.{ActorContext, typed}
|
import akka.actor.{ActorContext, typed}
|
||||||
import net.psforever.login.WorldSession.{CountAmmunition, CountGrenades, FindAmmoBoxThatUses, FindEquipmentStock, FindToolThatUses, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
import net.psforever.login.WorldSession.{CountAmmunition, CountGrenades, FindAmmoBoxThatUses, FindEquipmentStock, FindToolThatUses, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
||||||
|
import net.psforever.objects.OrbitalStrike.{cr4_os, cr5_os}
|
||||||
|
import net.psforever.objects.SpecialEmp.{cr3_emp, cr4_emp, cr5_emp}
|
||||||
import net.psforever.objects.ballistics.ProjectileQuality
|
import net.psforever.objects.ballistics.ProjectileQuality
|
||||||
import net.psforever.objects.definition.{ProjectileDefinition, SpecialExoSuitDefinition}
|
import net.psforever.objects.definition.{ProjectileDefinition, SpecialExoSuitDefinition}
|
||||||
import net.psforever.objects.entity.SimpleWorldEntity
|
import net.psforever.objects.entity.SimpleWorldEntity
|
||||||
|
|
@ -23,7 +25,9 @@ import net.psforever.objects.vital.etc.OicwLilBuddyReason
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
import net.psforever.objects.zones.exp.ToDatabase
|
import net.psforever.objects.zones.exp.ToDatabase
|
||||||
import net.psforever.types.{ChatMessageType, Vector3}
|
import net.psforever.packet.game.UplinkRequest
|
||||||
|
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||||
|
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, ValidPlanetSideGUID, Vector3}
|
||||||
import net.psforever.util.Config
|
import net.psforever.util.Config
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
@ -89,6 +93,7 @@ class WeaponAndProjectileOperations(
|
||||||
) extends CommonSessionInterfacingFunctionality {
|
) extends CommonSessionInterfacingFunctionality {
|
||||||
var shooting: mutable.Set[PlanetSideGUID] = mutable.Set.empty //ChangeFireStateMessage_Start
|
var shooting: mutable.Set[PlanetSideGUID] = mutable.Set.empty //ChangeFireStateMessage_Start
|
||||||
var prefire: mutable.Set[PlanetSideGUID] = mutable.Set.empty //if WeaponFireMessage precedes ChangeFireStateMessage_Start
|
var prefire: mutable.Set[PlanetSideGUID] = mutable.Set.empty //if WeaponFireMessage precedes ChangeFireStateMessage_Start
|
||||||
|
private[session] var orbitalStrikePos: Option[Vector3] = None
|
||||||
private[session] var shootingStart: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
private[session] var shootingStart: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
||||||
private[session] var shootingStop: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
private[session] var shootingStop: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
||||||
private[session] val shotsFired: mutable.HashMap[Int,Int] = mutable.HashMap[Int,Int]()
|
private[session] val shotsFired: mutable.HashMap[Int,Int] = mutable.HashMap[Int,Int]()
|
||||||
|
|
@ -292,6 +297,79 @@ class WeaponAndProjectileOperations(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def handleUplinkRequest(pkt: UplinkRequest): Unit = {
|
||||||
|
val UplinkRequest(code, pos, _) = pkt
|
||||||
|
val playerFaction = player.Faction
|
||||||
|
code match {
|
||||||
|
case UplinkRequestType.RevealFriendlies => ()
|
||||||
|
/*sendResponse(UplinkResponse(code.value, 0))
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 57, 10000)) //1200000
|
||||||
|
avatarActor ! AvatarActor.UpdateCUDTime("reveal_friendlies")*/
|
||||||
|
case UplinkRequestType.RevealEnemies => ()
|
||||||
|
/*sendResponse(UplinkResponse(code.value, 0))
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 58, 10000)) //1200000
|
||||||
|
avatarActor ! AvatarActor.UpdateCUDTime("reveal_enemies")
|
||||||
|
These are seen in a few logs, but didn't work. Unclear what these do or what '10' in Event1 represents
|
||||||
|
sendResponse(UplinkPositionEvent(5, Event0(5)))
|
||||||
|
sendResponse(UplinkPositionEvent(4, Event1(4, 10)))
|
||||||
|
sendResponse(UplinkPositionEvent(6, Event0(6)))*/
|
||||||
|
case UplinkRequestType.ElectroMagneticPulse =>
|
||||||
|
val cr = player.avatar.cr.value
|
||||||
|
val empSize = cr match {
|
||||||
|
case 3 => cr3_emp
|
||||||
|
case 4 => cr4_emp
|
||||||
|
case 5 => cr5_emp
|
||||||
|
}
|
||||||
|
val empColor = if (playerFaction != PlanetSideEmpire.NEUTRAL) { s"explosion_emp_${playerFaction.toString.toLowerCase}" } else { "explosion_emp_bo" }
|
||||||
|
sendResponse(UplinkResponse(code.value, 0))
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 59, 10000)) //1200000
|
||||||
|
avatarActor ! AvatarActor.UpdateCUDTime("emp_blast")
|
||||||
|
player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Zone.id}",
|
||||||
|
LocalAction.SendPacket(TriggerEffectMessage(ValidPlanetSideGUID(0), empColor, None, Some(TriggeredEffectLocation(player.Position, Vector3(0, 0, 90))))))
|
||||||
|
context.system.scheduler.scheduleOnce(delay = 1 seconds) {
|
||||||
|
Zone.serverSideDamage(player.Zone, player, empSize, SpecialEmp.createEmpInteraction(empSize, player.Position),
|
||||||
|
ExplosiveDeployableControl.detectionForExplosiveSource(player), Zone.findAllTargets)
|
||||||
|
}
|
||||||
|
case UplinkRequestType.OrbitalStrike =>
|
||||||
|
player.Zone.LocalEvents ! LocalServiceMessage(s"$playerFaction", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, pos.get.x, pos.get.y)))
|
||||||
|
sendResponse(UplinkResponse(code.value, 0))
|
||||||
|
orbitalStrikePos = pos
|
||||||
|
case UplinkRequestType.Unknown5 =>
|
||||||
|
val cr = player.avatar.cr.value
|
||||||
|
val strikeType = playerFaction match {
|
||||||
|
case PlanetSideEmpire.NC =>
|
||||||
|
if (cr == 4) {"explosion_bluedeath_nc"} else {"explosion_bluedeath_nc_lrg"}
|
||||||
|
case PlanetSideEmpire.TR =>
|
||||||
|
if (cr == 4) {"explosion_bluedeath_tr"} else {"explosion_bluedeath_tr_lrg"}
|
||||||
|
case PlanetSideEmpire.VS =>
|
||||||
|
if (cr == 4) {"explosion_bluedeath_vs"} else {"explosion_bluedeath_vs_lrg"}
|
||||||
|
case PlanetSideEmpire.NEUTRAL =>
|
||||||
|
if (cr == 4) {"explosion_bluedeath_bo"} else {"explosion_bluedeath_bo_lrg"}
|
||||||
|
}
|
||||||
|
val osSize = cr match {
|
||||||
|
case 4 => cr4_os
|
||||||
|
case 5 => cr5_os
|
||||||
|
}
|
||||||
|
sendResponse(UplinkResponse(code.value, 0))
|
||||||
|
sendResponse(PlanetsideAttributeMessage(player.GUID, 60, 10000)) //10800000
|
||||||
|
avatarActor ! AvatarActor.UpdateCUDTime("orbital_strike")
|
||||||
|
context.system.scheduler.scheduleOnce(delay = 5 seconds) {
|
||||||
|
player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Zone.id}",
|
||||||
|
LocalAction.SendPacket(TriggerEffectMessage(ValidPlanetSideGUID(0), strikeType, None, Some(TriggeredEffectLocation(orbitalStrikePos.get, Vector3(0, 0, 90))))))
|
||||||
|
player.Zone.LocalEvents ! LocalServiceMessage(s"$playerFaction", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, None)))
|
||||||
|
context.system.scheduler.scheduleOnce(delay = 5 seconds) {
|
||||||
|
val sectorTargets = Zone.findOrbitalStrikeTargets(player.Zone, orbitalStrikePos.get, osSize.DamageRadius, Zone.getOrbitbalStrikeTargets)
|
||||||
|
val withinRange = sectorTargets.filter {target => Zone.orbitalStrikeDistanceCheck(orbitalStrikePos.get, target.Position, osSize.DamageRadius)}
|
||||||
|
withinRange.foreach { target =>
|
||||||
|
target.Actor ! Vitality.Damage(DamageInteraction(SourceEntry(target), OrbitalStrike(PlayerSource(player)), target.Position).calculate())
|
||||||
|
}
|
||||||
|
orbitalStrikePos = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def handleChangeAmmo(pkt: ChangeAmmoMessage): Unit = {
|
def handleChangeAmmo(pkt: ChangeAmmoMessage): Unit = {
|
||||||
val ChangeAmmoMessage(item_guid, _) = pkt
|
val ChangeAmmoMessage(item_guid, _) = pkt
|
||||||
val (thing, equipment) = sessionLogic.findContainedEquipment()
|
val (thing, equipment) = sessionLogic.findContainedEquipment()
|
||||||
|
|
|
||||||
|
|
@ -2640,7 +2640,6 @@ class ZoningOperations(
|
||||||
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
|
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
|
||||||
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
|
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
|
||||||
}
|
}
|
||||||
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
|
||||||
setupAvatarFunc = AvatarCreate
|
setupAvatarFunc = AvatarCreate
|
||||||
//begin looking for conditions to set the avatar
|
//begin looking for conditions to set the avatar
|
||||||
context.system.scheduler.scheduleOnce(delay = 750 millisecond, context.self, SessionActor.SetCurrentAvatar(player, 200))
|
context.system.scheduler.scheduleOnce(delay = 750 millisecond, context.self, SessionActor.SetCurrentAvatar(player, 200))
|
||||||
|
|
@ -3304,6 +3303,7 @@ class ZoningOperations(
|
||||||
enqueueNewActivity(ActivityQueuedTask(ZoningOperations.reportProgressionSystem, 2))
|
enqueueNewActivity(ActivityQueuedTask(ZoningOperations.reportProgressionSystem, 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
61
src/main/scala/net/psforever/objects/OrbitalStrike.scala
Normal file
61
src/main/scala/net/psforever/objects/OrbitalStrike.scala
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright (c) 2025 PSForever
|
||||||
|
package net.psforever.objects
|
||||||
|
|
||||||
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
|
import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
|
||||||
|
import net.psforever.objects.vital.base.{DamageReason, DamageResolution, DamageType}
|
||||||
|
import net.psforever.objects.vital.damage.DamageCalculations
|
||||||
|
import net.psforever.objects.vital.prop.{DamageProperties, DamageWithPosition}
|
||||||
|
import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
|
||||||
|
|
||||||
|
final case class OrbitalStrike(player: PlayerSource)
|
||||||
|
extends DamageReason {
|
||||||
|
def resolution: DamageResolution.Value = DamageResolution.Hit
|
||||||
|
|
||||||
|
def same(test: DamageReason): Boolean = {
|
||||||
|
test.source eq source
|
||||||
|
}
|
||||||
|
|
||||||
|
def source: DamageProperties = OrbitalStrike.source
|
||||||
|
|
||||||
|
def damageModel: DamageAndResistance = OrbitalStrike.drm
|
||||||
|
|
||||||
|
override def adversary : Option[SourceEntry] = Some(player)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object OrbitalStrike {
|
||||||
|
|
||||||
|
final val cr4_os = new DamageWithPosition {
|
||||||
|
CausesDamageType = DamageType.Splash
|
||||||
|
SympatheticExplosion = true
|
||||||
|
Damage0 = 10000
|
||||||
|
DamageAtEdge = 0.1f
|
||||||
|
DamageRadius = 10f
|
||||||
|
}
|
||||||
|
|
||||||
|
final val cr5_os = new DamageWithPosition {
|
||||||
|
CausesDamageType = DamageType.Splash
|
||||||
|
SympatheticExplosion = true
|
||||||
|
Damage0 = 10000
|
||||||
|
DamageAtEdge = 0.1f
|
||||||
|
DamageRadius = 20f
|
||||||
|
}
|
||||||
|
|
||||||
|
private val source = new DamageProperties {
|
||||||
|
Damage0 = 10000
|
||||||
|
Damage1 = 10000
|
||||||
|
Damage2 = 10000
|
||||||
|
Damage3 = 10000
|
||||||
|
Damage4 = 10000
|
||||||
|
DamageToHealthOnly = true
|
||||||
|
DamageToVehicleOnly = true
|
||||||
|
DamageToBattleframeOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val drm = new DamageResistanceModel {
|
||||||
|
DamageUsing = DamageCalculations.AgainstExoSuit
|
||||||
|
ResistUsing = NoResistanceSelection
|
||||||
|
Model = SimpleResolutions.calculate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -65,6 +65,108 @@ object SpecialEmp {
|
||||||
innateDamage = emp
|
innateDamage = emp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final val cr3_emp = new DamageWithPosition {
|
||||||
|
CausesDamageType = DamageType.Splash
|
||||||
|
SympatheticExplosion = true
|
||||||
|
Damage0 = 0
|
||||||
|
DamageAtEdge = 1.0f
|
||||||
|
DamageRadius = 10f
|
||||||
|
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
|
||||||
|
Modifiers = MaxDistanceCutoff
|
||||||
|
}
|
||||||
|
|
||||||
|
final val cr4_emp = new DamageWithPosition {
|
||||||
|
CausesDamageType = DamageType.Splash
|
||||||
|
SympatheticExplosion = true
|
||||||
|
Damage0 = 0
|
||||||
|
DamageAtEdge = 1.0f
|
||||||
|
DamageRadius = 15f
|
||||||
|
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
|
||||||
|
Modifiers = MaxDistanceCutoff
|
||||||
|
}
|
||||||
|
|
||||||
|
final val cr5_emp = new DamageWithPosition {
|
||||||
|
CausesDamageType = DamageType.Splash
|
||||||
|
SympatheticExplosion = true
|
||||||
|
Damage0 = 0
|
||||||
|
DamageAtEdge = 1.0f
|
||||||
|
DamageRadius = 20f
|
||||||
|
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
|
||||||
|
Modifiers = MaxDistanceCutoff
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger an electromagnetic pulse.
|
* Trigger an electromagnetic pulse.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,13 @@ object Avatar {
|
||||||
GlobalDefinitions.trhev_pounder -> 5.minutes
|
GlobalDefinitions.trhev_pounder -> 5.minutes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val cudCooldowns: Map[String, FiniteDuration] = Map(
|
||||||
|
"orbital_strike" -> 1.minutes, // 3.hours
|
||||||
|
"emp_blast" -> 1.minutes, // 20.minutes
|
||||||
|
"reveal_friendlies" -> 1.minutes, // 20.minutes
|
||||||
|
"reveal_enemies" -> 1.minutes // 20.minutes
|
||||||
|
)
|
||||||
|
|
||||||
val useCooldowns: Map[BasicDefinition, FiniteDuration] = Map(
|
val useCooldowns: Map[BasicDefinition, FiniteDuration] = Map(
|
||||||
GlobalDefinitions.medkit -> 5.seconds,
|
GlobalDefinitions.medkit -> 5.seconds,
|
||||||
GlobalDefinitions.super_armorkit -> 20.minutes,
|
GlobalDefinitions.super_armorkit -> 20.minutes,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import net.psforever.objects.vital.collision.CollisionReason
|
||||||
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
|
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
|
import org.joda.time.{LocalDateTime, Seconds}
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
|
@ -359,6 +360,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
equipment match {
|
equipment match {
|
||||||
case Some(holsteredEquipment) =>
|
case Some(holsteredEquipment) =>
|
||||||
log.info(s"${player.Name} has put ${player.Sex.possessive} ${holsteredEquipment.Definition.Name} down")
|
log.info(s"${player.Name} has put ${player.Sex.possessive} ${holsteredEquipment.Definition.Name} down")
|
||||||
|
//make sure the player didn't just initialte an orbital strike. If not (the if below is true), make sure waypoint is removed
|
||||||
|
if (holsteredEquipment.Definition == GlobalDefinitions.command_detonater && player.avatar.cr.value > 3 &&
|
||||||
|
!player.avatar.cooldowns.purchase.exists(os => os._1 == "orbital_strike" && Seconds.secondsBetween(os._2, LocalDateTime.now()).getSeconds < 10)) {
|
||||||
|
player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Faction}", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, None)))
|
||||||
|
}
|
||||||
case None =>
|
case None =>
|
||||||
log.info(s"${player.Name} lowers ${player.Sex.possessive} hand")
|
log.info(s"${player.Name} lowers ${player.Sex.possessive} hand")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1845,6 +1845,53 @@ object Zone {
|
||||||
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
|
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 findOrbitalStrikeTargets(
|
||||||
|
zone: Zone,
|
||||||
|
sourcePosition: Vector3,
|
||||||
|
radius: Float,
|
||||||
|
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
|
||||||
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
|
getTargetsFromSector(zone.blockMap.sector(sourcePosition.xy, radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
def getOrbitbalStrikeTargets(sector: SectorPopulation): List[PlanetSideServerObject with Vitality] = {
|
||||||
|
//collect all targets that can be damaged
|
||||||
|
//players
|
||||||
|
val playerTargets = sector.livePlayerList.filter { player => player.VehicleSeated.isEmpty && player.WhichSide == Sidedness.OutsideOf }
|
||||||
|
//vehicles
|
||||||
|
val vehicleTargets = sector.vehicleList.filterNot { _.Destroyed }
|
||||||
|
//deployables
|
||||||
|
val deployableTargets = sector.deployableList.filter { obj => !obj.Destroyed && obj.WhichSide == Sidedness.OutsideOf }
|
||||||
|
//amenities
|
||||||
|
val soiTargets = sector.amenityList.collect {
|
||||||
|
case amenity: Vitality if !amenity.Destroyed && amenity.WhichSide == Sidedness.OutsideOf && amenity.CanDamage => amenity }
|
||||||
|
//altogether ...
|
||||||
|
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if targets returned from sector are within range of the imminent Orbital Strike
|
||||||
|
* @param p1 OS position
|
||||||
|
* @param p2 target position
|
||||||
|
* @param maxDistance radius of the Orbital Strike
|
||||||
|
* @return `true`, if the two entities are near enough to each other;
|
||||||
|
* `false`, otherwise
|
||||||
|
*/
|
||||||
|
def orbitalStrikeDistanceCheck(p1: Vector3, p2: Vector3, maxDistance: Float): Boolean = {
|
||||||
|
val radius = maxDistance * maxDistance
|
||||||
|
Vector3.DistanceSquared(p1.xy, p2.xy) <= radius
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* na
|
* na
|
||||||
* @param instigation what previous event happened, if any, that caused this explosion
|
* @param instigation what previous event happened, if any, that caused this explosion
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,10 @@ import scodec.codecs._
|
||||||
* </ul>
|
* </ul>
|
||||||
* `55 - "Someone is attempting to Heal you". Value is 1`<br>
|
* `55 - "Someone is attempting to Heal you". Value is 1`<br>
|
||||||
* `56 - "Someone is attempting to Repair you". Value is 1`<br>
|
* `56 - "Someone is attempting to Repair you". Value is 1`<br>
|
||||||
|
* `57 - CUD Reveal Friendlies cooldown timer`<br>
|
||||||
|
* `58 - CUD Reveal Enemies cooldown timer`<br>
|
||||||
|
* `59 - CUD EMP Blast cooldown timer`<br>
|
||||||
|
* `60 - CUD Orbital Strike cooldown timer`<br>
|
||||||
* `64 - ????? related to using router telepads`
|
* `64 - ????? related to using router telepads`
|
||||||
* `67 - Enables base shields (from cavern module/lock)`<br>
|
* `67 - Enables base shields (from cavern module/lock)`<br>
|
||||||
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`<br>
|
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`<br>
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,15 @@ object UplinkRequestType extends IntEnum[UplinkRequestType] {
|
||||||
|
|
||||||
case object OrbitalStrike extends UplinkRequestType(value = 4)
|
case object OrbitalStrike extends UplinkRequestType(value = 4)
|
||||||
|
|
||||||
case object Unknown5 extends UplinkRequestType(value = 5)
|
case object Unknown5 extends UplinkRequestType(value = 5) // pull trigger to start orbital strike countdown
|
||||||
|
|
||||||
case object Function6 extends UplinkRequestType(value = 6)
|
case object Function6 extends UplinkRequestType(value = 6)
|
||||||
|
|
||||||
case object Function7 extends UplinkRequestType(value = 7)
|
case object Function7 extends UplinkRequestType(value = 7) // sent back by client after reveal enemies response
|
||||||
|
|
||||||
case object Function8 extends UplinkRequestType(value = 8)
|
case object Function8 extends UplinkRequestType(value = 8)
|
||||||
|
|
||||||
case object Unknown9 extends UplinkRequestType(value = 9)
|
case object Unknown9 extends UplinkRequestType(value = 9) // recall squad to sanc
|
||||||
|
|
||||||
case object UnknownA extends UplinkRequestType(value = 10)
|
case object UnknownA extends UplinkRequestType(value = 10)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue