MAX Capacitors (#297)

* fix isAnchored / isOverdrived faction check

* Faction specific MAX definitions

* GenericActionMessage documentation

* MAX Capacitor functionality and changes to damage resolution for NC MAX shield
This commit is contained in:
Mazo 2019-12-10 13:53:11 +00:00 committed by Fate-JH
parent d168c40093
commit 28beea4e30
10 changed files with 351 additions and 56 deletions

View file

@ -26,23 +26,26 @@ import scala.collection.mutable
import scala.concurrent.duration._
object GlobalDefinitions {
/*
characters
*/
// Characters
val avatar = new AvatarDefinition(121)
/*
exo-suits
*/
val Standard = ExoSuitDefinition(ExoSuitType.Standard)
val Agile = ExoSuitDefinition(ExoSuitType.Agile)
val Reinforced = ExoSuitDefinition(ExoSuitType.Reinforced)
val Infiltration = ExoSuitDefinition(ExoSuitType.Infiltration)
val MAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
val VSMAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
val TRMAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
val NCMAX = SpecialExoSuitDefinition(ExoSuitType.MAX)
init_exosuit()
/*
Implants
*/
@ -1527,7 +1530,7 @@ object GlobalDefinitions {
Standard.ResistanceSplash = 15
Standard.ResistanceAggravated = 8
Agile.Name = "lite_armor"
Agile.Name = "agile"
Agile.MaxArmor = 100
Agile.InventoryScale = InventoryTile.Tile99
Agile.InventoryOffset = 6
@ -1539,7 +1542,7 @@ object GlobalDefinitions {
Agile.ResistanceSplash = 25
Agile.ResistanceAggravated = 10
Reinforced.Name = "med_armor"
Reinforced.Name = "reinforced"
Reinforced.Permissions = List(CertificationType.ReinforcedExoSuit)
Reinforced.MaxArmor = 200
Reinforced.InventoryScale = InventoryTile.Tile1209
@ -1561,18 +1564,39 @@ object GlobalDefinitions {
Infiltration.Holster(0, EquipmentSize.Pistol)
Infiltration.Holster(4, EquipmentSize.Melee)
MAX.Permissions = List(CertificationType.AIMAX,CertificationType.AVMAX, CertificationType.AAMAX, CertificationType.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.Damage = StandardMaxDamage
MAX.Model = StandardResolutions.Max
def CommonMaxConfig(max : SpecialExoSuitDefinition): Unit = {
max.Permissions = List(CertificationType.AIMAX,CertificationType.AVMAX, CertificationType.AAMAX, CertificationType.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.Damage = StandardMaxDamage
max.Model = StandardResolutions.Max
}
CommonMaxConfig(VSMAX)
VSMAX.MaxCapacitor = 50
VSMAX.CapacitorRechargeDelayMillis = 5000
VSMAX.CapacitorRechargePerSecond = 3
VSMAX.CapacitorDrainPerSecond = 20
CommonMaxConfig(TRMAX)
TRMAX.MaxCapacitor = 300
TRMAX.CapacitorRechargeDelayMillis = 10000
TRMAX.CapacitorRechargePerSecond = 10
TRMAX.CapacitorDrainPerSecond = 30
CommonMaxConfig(NCMAX)
NCMAX.MaxCapacitor = 400
NCMAX.CapacitorRechargeDelayMillis = 10000
NCMAX.CapacitorRechargePerSecond = 4
NCMAX.CapacitorDrainPerSecond = 4
}
/**
* Initialize `AmmoBoxDefinition` globals.

View file

@ -27,6 +27,12 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
private var health : Int = 0
private var stamina : Int = 0
private var armor : Int = 0
private var capacitor : Float = 0f
private var capacitorState : CapacitorStateType.Value = CapacitorStateType.Idle
private var capacitorLastUsedMillis : Long = 0
private var capacitorLastChargedMillis : Long = 0
private var maxHealth : Int = 100 //TODO affected by empire benefits, territory benefits, and bops
private var maxStamina : Int = 100 //does anything affect this?
@ -87,6 +93,7 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
Health = MaxHealth
Stamina = MaxStamina
Armor = MaxArmor
Capacitor = 0
ResetAllImplants()
}
isAlive
@ -151,6 +158,44 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
def MaxArmor : Int = exosuit.MaxArmor
def Capacitor : Float = capacitor
def Capacitor_=(value : Float) : Float = {
val newValue = math.min(math.max(0, value), ExoSuitDef.MaxCapacitor)
if(newValue < capacitor) {
capacitorLastUsedMillis = System.currentTimeMillis()
capacitorLastChargedMillis = 0
}
else if(newValue > capacitor && newValue < ExoSuitDef.MaxCapacitor) {
capacitorLastChargedMillis = System.currentTimeMillis()
capacitorLastUsedMillis = 0
}
else if(newValue > capacitor && newValue == ExoSuitDef.MaxCapacitor) {
capacitorLastChargedMillis = 0
capacitorLastUsedMillis = 0
capacitorState = CapacitorStateType.Idle
}
capacitor = newValue
capacitor
}
def CapacitorState : CapacitorStateType.Value = capacitorState
def CapacitorState_=(value : CapacitorStateType.Value) : CapacitorStateType.Value = {
value match {
case CapacitorStateType.Charging => capacitorLastChargedMillis = System.currentTimeMillis()
case CapacitorStateType.Discharging => capacitorLastUsedMillis = System.currentTimeMillis()
case _ => ;
}
capacitorState = value
capacitorState
}
def CapacitorLastUsedMillis = capacitorLastUsedMillis
def CapacitorLastChargedMillis = capacitorLastChargedMillis
def VisibleSlots : Set[Int] = if(exosuit.SuitType == ExoSuitType.MAX) {
Set(0)
}
@ -290,9 +335,10 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
def LastDrawnSlot : Int = lastDrawnSlot
def ExoSuit : ExoSuitType.Value = exosuit.SuitType
def ExoSuitDef : ExoSuitDefinition = exosuit
def ExoSuit_=(suit : ExoSuitType.Value) : Unit = {
val eSuit = ExoSuitDefinition.Select(suit)
val eSuit = ExoSuitDefinition.Select(suit, Faction)
exosuit = eSuit
Player.SuitSetup(this, eSuit)
ChangeSpecialAbility()
@ -509,9 +555,9 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
SpecialExoSuitDefinition.Mode.Normal
}
def isAnchored : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored
def isAnchored : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.TR && UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored
def isOverdrived : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Overdrive
def isOverdrived : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.TR && UsingSpecial == SpecialExoSuitDefinition.Mode.Overdrive
def isShielded : Boolean = ExoSuit == ExoSuitType.MAX && Faction == PlanetSideEmpire.NC && UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded

View file

@ -6,7 +6,7 @@ import net.psforever.objects.equipment.EquipmentSize
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.vital._
import net.psforever.objects.vital.resistance.ResistanceProfileMutators
import net.psforever.types.{CertificationType, ExoSuitType}
import net.psforever.types.{CertificationType, ExoSuitType, PlanetSideEmpire}
/**
* A definition for producing the personal armor the player wears.
@ -21,6 +21,10 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicD
protected val holsters : Array[EquipmentSize.Value] = Array.fill[EquipmentSize.Value](5)(EquipmentSize.Blocked)
protected var inventoryScale : InventoryTile = InventoryTile.Tile11 //override with custom InventoryTile
protected var inventoryOffset : Int = 0
protected var maxCapacitor : Int = 0
protected var capacitorRechargeDelayMillis : Int = 0
protected var capacitorRechargePerSecond : Int = 0
protected var capacitorDrainPerSecond : Int = 0
Name = "exo-suit"
Damage = StandardInfantryDamage
Resistance = StandardInfantryResistance
@ -35,6 +39,34 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicD
MaxArmor
}
def MaxCapacitor : Int = maxCapacitor
def MaxCapacitor_=(value : Int) : Int = {
maxCapacitor = value
maxCapacitor
}
def CapacitorRechargeDelayMillis : Int = capacitorRechargeDelayMillis
def CapacitorRechargeDelayMillis_=(value : Int) : Int = {
capacitorRechargeDelayMillis = value
capacitorRechargeDelayMillis
}
def CapacitorRechargePerSecond : Int = capacitorRechargePerSecond
def CapacitorRechargePerSecond_=(value : Int) : Int = {
capacitorRechargePerSecond = value
capacitorRechargePerSecond
}
def CapacitorDrainPerSecond : Int = capacitorDrainPerSecond
def CapacitorDrainPerSecond_=(value : Int) : Int = {
capacitorDrainPerSecond = value
capacitorDrainPerSecond
}
def InventoryScale : InventoryTile = inventoryScale
def InventoryScale_=(scale : InventoryTile) : InventoryTile = {
@ -96,6 +128,10 @@ class SpecialExoSuitDefinition(private val suitType : ExoSuitType.Value) extends
val obj = new SpecialExoSuitDefinition(SuitType)
obj.Permissions = Permissions
obj.MaxArmor = MaxArmor
obj.MaxCapacitor = MaxCapacitor
obj.CapacitorRechargePerSecond = CapacitorRechargePerSecond
obj.CapacitorDrainPerSecond = CapacitorDrainPerSecond
obj.CapacitorRechargeDelayMillis = CapacitorRechargeDelayMillis
obj.InventoryScale = InventoryScale
obj.InventoryOffset = InventoryOffset
obj.Subtract.Damage0 = Subtract.Damage0
@ -138,14 +174,19 @@ object ExoSuitDefinition {
/**
* A function to retrieve the correct defintion of an exo-suit from the type of exo-suit.
* @param suit the `Enumeration` corresponding to this exo-suit
* @param faction the faction the player belongs to for this exosuit
* @return the exo-suit definition
*/
def Select(suit : ExoSuitType.Value) : ExoSuitDefinition = {
def Select(suit : ExoSuitType.Value, faction : PlanetSideEmpire.Value) : ExoSuitDefinition = {
suit match {
case ExoSuitType.Agile => GlobalDefinitions.Agile.Use
case ExoSuitType.Infiltration => GlobalDefinitions.Infiltration.Use
case ExoSuitType.MAX => GlobalDefinitions.MAX.Use
case ExoSuitType.Agile => GlobalDefinitions.Agile.Use
case ExoSuitType.Reinforced => GlobalDefinitions.Reinforced.Use
case ExoSuitType.MAX => faction match {
case PlanetSideEmpire.TR => GlobalDefinitions.TRMAX.Use
case PlanetSideEmpire.NC => GlobalDefinitions.NCMAX.Use
case PlanetSideEmpire.VS => GlobalDefinitions.VSMAX.Use
}
case _ => GlobalDefinitions.Standard.Use
}
}

View file

@ -106,13 +106,13 @@ object ResistanceCalculations {
//extractors
def NoResistExtractor(target : SourceEntry) : Int = 0
def ExoSuitDirectExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit).ResistanceDirectHit
def ExoSuitDirectExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit, target.Faction).ResistanceDirectHit
def ExoSuitSplashExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit).ResistanceSplash
def ExoSuitSplashExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit, target.Faction).ResistanceSplash
def ExoSuitAggravatedExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit).ResistanceAggravated
def ExoSuitAggravatedExtractor(target : PlayerSource) : Int = ExoSuitDefinition.Select(target.ExoSuit, target.Faction).ResistanceAggravated
def ExoSuitRadiationExtractor(target : PlayerSource) : Float = ExoSuitDefinition.Select(target.ExoSuit).RadiationShielding
def ExoSuitRadiationExtractor(target : PlayerSource) : Float = ExoSuitDefinition.Select(target.ExoSuit, target.Faction).RadiationShielding
def VehicleDirectExtractor(target : VehicleSource) : Int = target.Definition.ResistanceDirectHit

View file

@ -109,27 +109,53 @@ object ResolutionCalculations {
def NoApplication(damageValue : Int, data : ResolvedProjectile)(target : Any) : Unit = { }
def SubtractWithRemainder(current : Int, damage : Int) : (Int, Int) = {
val a = Math.max(0, current - damage)
val remainingDamage = Math.abs(current - damage - a)
(a, remainingDamage)
}
/**
* The expanded `(Any)=>Unit` function for infantry.
* Apply the damage values to the health field and personal armor field for an infantry target.
* Apply the damage values to the capacitor (if shielded NC max), health field and personal armor field for an infantry target.
* @param damageValues a tuple containing damage values for: health, personal armor
* @param data the historical `ResolvedProjectile` information
* @param target the `Player` object to be affected by these damage values (at some point)
*/
def InfantryApplication(damageValues : (Int, Int), data : ResolvedProjectile)(target : Any) : Unit = target match {
case player : Player =>
val (a, b) = damageValues
var (a, b) = damageValues
var result = (0, 0)
//TODO Personal Shield implant test should go here and modify the values a and b
if(player.isAlive && !(a == 0 && b == 0)) {
player.History(data)
if(player.Armor - b < 0) {
player.Health = player.Health - a - (b - player.Armor)
player.Armor = 0
}
else {
player.Armor = player.Armor - b
player.Health = player.Health - a
if(player.Capacitor.toInt > 0 && player.isShielded) {
// Subtract armour damage from capacitor
result = SubtractWithRemainder(player.Capacitor.toInt, b)
player.Capacitor = result._1
b = result._2
// Then follow up with health damage if any capacitor is left
result = SubtractWithRemainder(player.Capacitor.toInt, a)
player.Capacitor = result._1
a = result._2
}
// Subtract any remaining armour damage from armour
result = SubtractWithRemainder(player.Armor, b)
player.Armor = result._1
b = result._2
// Then bleed through to health if armour ran out
result = SubtractWithRemainder(player.Health, b)
player.Health = result._1
b = result._2
// Finally, apply health damage to health
result = SubtractWithRemainder(player.Health, a)
player.Health = result._1
a = result._2
}
case _ =>
}

View file

@ -34,10 +34,15 @@ import scodec.codecs._
* 45 - ?<br>
* <br>
* Actions (when sent from client):<br>
* 15 - Max anchor
* 16 - Max unanchor
* 20 - Client requests MAX special effect (NC shield and TR overdrive. VS jump jets are handled by the jump_thrust boolean on PlayerStateMessageUpstream)
* 21 - Disable MAX special effect (NC shield)
* 29 - AFK<br>
* 30 - back in game<br>
* 36 - turn on "Looking for Squad"<br>
* 37 - turn off "Looking for Squad"
*
* @param action what this packet does
*/
final case class GenericActionMessage(action : Int)

View file

@ -0,0 +1,10 @@
package net.psforever.types
object CapacitorStateType extends Enumeration {
type Type = Value
val Idle = Value(0)
val Charging = Value(1)
val ChargeDelay = Value(2)
val Discharging = Value(3)
}

View file

@ -223,7 +223,7 @@ class ResolutionCalculationsTests extends Specification {
InfantryDamageAfterResist(100,100)(50, 10) mustEqual (40,10)
}
"calculate health and armor damage, with bonus damage, for infantry target" in {
"calculate health and armor damage, with bleed through damage, for infantry target" in {
//health = 100, armor = 5 -> resist 10 but only have 5, so rollover extra -> damages (40+5, 5)
InfantryDamageAfterResist(100,5)(50, 10) mustEqual (45,5)
}

View file

@ -1,10 +1,11 @@
// Copyright (c) 2017 PSForever
package objects
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.{ExoSuitDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
import net.psforever.types.ExoSuitType
import net.psforever.types.{ExoSuitType, PlanetSideEmpire}
import org.specs2.mutable._
class ExoSuitTest extends Specification {
@ -127,15 +128,47 @@ class ExoSuitTest extends Specification {
"ExoSuitDefinition.Select" should {
"produce common, shared instances of exo suits" in {
ExoSuitDefinition.Select(ExoSuitType.Standard) eq ExoSuitDefinition.Select(ExoSuitType.Standard)
ExoSuitDefinition.Select(ExoSuitType.Agile) eq ExoSuitDefinition.Select(ExoSuitType.Agile)
ExoSuitDefinition.Select(ExoSuitType.Reinforced) eq ExoSuitDefinition.Select(ExoSuitType.Reinforced)
ExoSuitDefinition.Select(ExoSuitType.Infiltration) eq ExoSuitDefinition.Select(ExoSuitType.Infiltration)
ExoSuitDefinition.Select(ExoSuitType.Standard, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Standard, PlanetSideEmpire.VS)
ExoSuitDefinition.Select(ExoSuitType.Agile, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Agile, PlanetSideEmpire.VS)
ExoSuitDefinition.Select(ExoSuitType.Reinforced, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Reinforced, PlanetSideEmpire.VS)
ExoSuitDefinition.Select(ExoSuitType.Infiltration, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Infiltration, PlanetSideEmpire.VS)
}
"produce common, shared instances of exo suits across factions" in {
ExoSuitDefinition.Select(ExoSuitType.Standard, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Standard, PlanetSideEmpire.TR)
ExoSuitDefinition.Select(ExoSuitType.Standard, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Standard, PlanetSideEmpire.NC)
ExoSuitDefinition.Select(ExoSuitType.Agile, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Agile, PlanetSideEmpire.TR)
ExoSuitDefinition.Select(ExoSuitType.Agile, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Agile, PlanetSideEmpire.NC)
ExoSuitDefinition.Select(ExoSuitType.Reinforced, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Reinforced, PlanetSideEmpire.TR)
ExoSuitDefinition.Select(ExoSuitType.Reinforced, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Reinforced, PlanetSideEmpire.NC)
ExoSuitDefinition.Select(ExoSuitType.Infiltration, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Infiltration, PlanetSideEmpire.TR)
ExoSuitDefinition.Select(ExoSuitType.Infiltration, PlanetSideEmpire.VS) eq ExoSuitDefinition.Select(ExoSuitType.Infiltration, PlanetSideEmpire.NC)
}
"can select max for TR" in {
val obj = ExoSuitDefinition.Select(ExoSuitType.MAX, PlanetSideEmpire.TR)
obj.isInstanceOf[SpecialExoSuitDefinition] mustEqual true
obj.MaxCapacitor mustEqual 300
}
"can select max for VS" in {
val obj = ExoSuitDefinition.Select(ExoSuitType.MAX, PlanetSideEmpire.VS)
obj.isInstanceOf[SpecialExoSuitDefinition] mustEqual true
obj.MaxCapacitor mustEqual 50
}
"can select max for NC" in {
val obj = ExoSuitDefinition.Select(ExoSuitType.MAX, PlanetSideEmpire.NC)
obj.isInstanceOf[SpecialExoSuitDefinition] mustEqual true
obj.MaxCapacitor mustEqual 400
}
"produces unique instances of the mechanized assault exo suit" in {
val obj = ExoSuitDefinition.Select(ExoSuitType.MAX)
obj ne ExoSuitDefinition.Select(ExoSuitType.MAX)
val obj = ExoSuitDefinition.Select(ExoSuitType.MAX, PlanetSideEmpire.VS)
obj ne ExoSuitDefinition.Select(ExoSuitType.MAX, PlanetSideEmpire.VS)
obj.isInstanceOf[SpecialExoSuitDefinition] mustEqual true
}
}

View file

@ -1269,14 +1269,30 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(player.isAlive) {
val originalHealth = player.Health
val originalArmor = player.Armor
val originalCapacitor = player.Capacitor.toInt
resolution_function(target)
val health = player.Health
val armor = player.Armor
val capacitor = player.Capacitor.toInt
val damageToHealth = originalHealth - health
val damageToArmor = originalArmor - armor
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor, AFTER=$health/$armor, CHANGE=$damageToHealth/$damageToArmor")
if(damageToHealth != 0 || damageToArmor != 0) {
val damageToCapacitor = originalCapacitor - capacitor
damageLog.info(s"${player.Name}-infantry: BEFORE=$originalHealth/$originalArmor/$originalCapacitor, AFTER=$health/$armor/$capacitor, CHANGE=$damageToHealth/$damageToArmor/$damageToCapacitor")
if(damageToHealth != 0 || damageToArmor != 0 || damageToCapacitor != 0) {
val playerGUID = player.GUID
if(damageToHealth > 0) {
sendResponse(PlanetsideAttributeMessage(playerGUID, 0, health))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 0, health))
}
if(damageToArmor > 0) {
sendResponse(PlanetsideAttributeMessage(playerGUID, 4, armor))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 4, armor))
}
if(damageToCapacitor > 0) {
sendResponse(PlanetsideAttributeMessage(playerGUID, 7, capacitor))
}
sendResponse(PlanetsideAttributeMessage(playerGUID, 0, health))
sendResponse(PlanetsideAttributeMessage(playerGUID, 4, armor))
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(playerGUID, 0, health))
@ -2067,7 +2083,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val fallbackSubtype = 0
//a loadout with a prohibited exo-suit type will result in a fallback exo-suit type
val (nextSuit : ExoSuitType.Value, nextSubtype : Int) =
if(ExoSuitDefinition.Select(exosuit).Permissions match {
if(ExoSuitDefinition.Select(exosuit, player.Faction).Permissions match {
case Nil =>
true
case permissions if subtype != 0 =>
@ -2122,7 +2138,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
else {
val newSuitDef = ExoSuitDefinition.Select(nextSuit)
val newSuitDef = ExoSuitDefinition.Select(nextSuit, player.Faction)
val (afterInventory, extra) = GridInventory.recoverInventory(
inventory.filterNot(dropPred),
tplayer.Inventory
@ -3911,6 +3927,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
else {
player.Stamina > 0
}
CapacitorTick(jump_thrust)
}
else {
timeDL = 0
@ -5347,7 +5365,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
tool.ToFireMode = convertFireModeIndex
sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex))
case _ =>
log.info(s"GenericObject: $player is MAX with an unexpected weapon - ${definition.Name}")
log.warn(s"GenericObject: $player is MAX with an unexpected weapon - ${definition.Name}")
}
}
else if(action == 16) { //max deployment
@ -5365,7 +5383,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
tool.ToFireMode = convertFireModeIndex
sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex))
case _ =>
log.info(s"GenericObject: $player is MAX with an unexpected weapon - ${definition.Name}")
log.warn(s"GenericObject: $player is MAX with an unexpected weapon - ${definition.Name}")
}
}
else if (action == 20) {
if(player.ExoSuit == ExoSuitType.MAX) {
ToggleMaxSpecialState(enable = true)
} else {
log.warn("Got GenericActionMessage 20 but can't handle it")
}
}
else if (action == 21) {
if(player.ExoSuit == ExoSuitType.MAX) {
player.Faction match {
case PlanetSideEmpire.NC =>
ToggleMaxSpecialState(enable = false)
case _ => log.warn(s"Player ${player.Name} tried to cancel an uncancellable MAX special ability")
}
} else {
log.warn("Got GenericActionMessage 21 but can't handle it")
}
}
else if(action == 36) { //Looking For Squad ON
@ -5462,6 +5498,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
log.info(s"WeaponFire: $msg")
if(player.isShielded) {
// Cancel NC MAX shield if it's active
ToggleMaxSpecialState(enable = false)
}
FindContainedWeapon match {
case (Some(obj), Some(tool : Tool)) =>
if(tool.Magazine <= 0) { //safety: enforce ammunition depletion
@ -7807,6 +7847,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val player_guid = tplayer.GUID
val pos = tplayer.Position
val respawnTimer = 300000 //milliseconds
ToggleMaxSpecialState(enable = false)
tplayer.Die
deadState = DeadState.Dead
timeDL = 0
@ -10102,6 +10143,75 @@ class WorldSessionActor extends Actor with MDCContextAware {
squadUpdateCounter = (squadUpdateCounter + 1) % queuedSquadActions.length
}
def CapacitorTick(jump_thrust : Boolean): Unit = {
if(player.ExoSuit == ExoSuitType.MAX) {
//Discharge
if(jump_thrust || player.isOverdrived || player.isShielded) {
if(player.CapacitorState == CapacitorStateType.Discharging) {
// Previous tick was already discharging, calculate how much energy to drain from time between the two ticks
val timeDiff = (System.currentTimeMillis() - player.CapacitorLastUsedMillis).toFloat / 1000
val drainAmount = player.ExoSuitDef.CapacitorDrainPerSecond.toFloat * timeDiff
player.Capacitor -= drainAmount
sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt))
} else {
// Start discharging
player.CapacitorState = CapacitorStateType.Discharging
}
}
// Charge
else if(player.Capacitor < player.ExoSuitDef.MaxCapacitor
&& (player.CapacitorState == CapacitorStateType.Idle || player.CapacitorState == CapacitorStateType.Charging || (player.CapacitorState == CapacitorStateType.ChargeDelay && System.currentTimeMillis() - player.CapacitorLastUsedMillis > player.ExoSuitDef.CapacitorRechargeDelayMillis)))
{
if(player.CapacitorState == CapacitorStateType.Charging) {
val timeDiff = (System.currentTimeMillis() - player.CapacitorLastChargedMillis).toFloat / 1000
val chargeAmount = player.ExoSuitDef.CapacitorRechargePerSecond * timeDiff
player.Capacitor += chargeAmount
sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt))
} else {
player.CapacitorState = CapacitorStateType.Charging
}
}
if(player.Faction == PlanetSideEmpire.VS) {
// Start charge delay for VS when not boosting
if(!jump_thrust && player.CapacitorState == CapacitorStateType.Discharging ) {
player.CapacitorState = CapacitorStateType.ChargeDelay
}
}
else {
// Start charge delay for other factions if capacitor is empty or special ability is off
if(player.CapacitorState == CapacitorStateType.Discharging && (player.Capacitor == 0 || (!player.isOverdrived && !player.isShielded))) {
player.CapacitorState = CapacitorStateType.ChargeDelay
ToggleMaxSpecialState(enable = false)
}
}
}
else {
if(player.CapacitorState != CapacitorStateType.Idle) {
player.CapacitorState = CapacitorStateType.Idle
}
}
}
def ToggleMaxSpecialState(enable : Boolean): Unit = {
if(player.ExoSuit == ExoSuitType.MAX) {
if(enable) {
player.Faction match {
case PlanetSideEmpire.TR => if(player.Capacitor == player.ExoSuitDef.MaxCapacitor) player.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive
case PlanetSideEmpire.NC => if(player.Capacitor > 0) player.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded
case _ => log.warn(s"Player ${player.Name} tried to use a MAX special ability but their faction doesn't have one")
}
if(player.UsingSpecial == SpecialExoSuitDefinition.Mode.Overdrive || player.UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded) {
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttributeToAll(player.GUID, 8, 1))
}
}
else {
player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttributeToAll(player.GUID, 8, 0))
}
}
/**
* The main purpose of this method is to determine which targets will receive "locked on" warnings from remote projectiles.
* For a given series of globally unique identifiers, indicating targets,