diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index e5da7c05..f833b0fb 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -463,6 +463,12 @@ object GlobalDefinitions {
val flamethrower_ammo = AmmoBoxDefinition(Ammo.flamethrower_ammo)
+ val winchester_ammo = AmmoBoxDefinition(Ammo.winchester_ammo)
+
+ val pellet_gun_ammo = AmmoBoxDefinition(Ammo.pellet_gun_ammo)
+
+ val six_shooter_ammo = AmmoBoxDefinition(Ammo.six_shooter_ammo)
+
val dualcycler_ammo = AmmoBoxDefinition(Ammo.dualcycler_ammo)
val pounder_ammo = AmmoBoxDefinition(Ammo.pounder_ammo)
@@ -652,6 +658,14 @@ object GlobalDefinitions {
val flamethrower = ToolDefinition(ObjectClass.flamethrower)
+ val winchester = ToolDefinition(ObjectClass.winchester)
+
+ val pellet_gun = ToolDefinition(ObjectClass.pellet_gun)
+
+ val six_shooter = ToolDefinition(ObjectClass.six_shooter)
+
+ val dynomite = ToolDefinition(ObjectClass.dynomite)
+
val trhev_dualcycler = new ToolDefinition(ObjectClass.trhev_dualcycler) {
override def NextFireModeIndex(index : Int) : Int = index
}
@@ -1204,6 +1218,21 @@ object GlobalDefinitions {
case PlanetSideEmpire.NEUTRAL => bullet_9mm
}
}
+ /**
+ * For a given faction, provide the AP ammunition for the medium assault rifle.
+ * The ammunition value here must work with the result of obtaining the rifle using the faction.
+ * @param faction the faction
+ * @return thr `AmmoBoxDefinition` for the rifle's ammo
+ * @see `GlobalDefinitions.MediumRifle`
+ */
+ def MediumRifleAPAmmo(faction : PlanetSideEmpire.Value) : AmmoBoxDefinition = {
+ faction match {
+ case PlanetSideEmpire.TR => bullet_9mm_AP
+ case PlanetSideEmpire.NC => bullet_9mm_AP
+ case PlanetSideEmpire.VS => energy_cell
+ case PlanetSideEmpire.NEUTRAL => bullet_9mm_AP
+ }
+ }
/**
* For a given faction, provide the heavy assault rifle.
@@ -1236,6 +1265,22 @@ object GlobalDefinitions {
}
}
+ /**
+ * For a given faction, provide the AP ammunition for the heavy assault rifle.
+ * The ammunition value here must work with the result of obtaining the rifle using the faction.
+ * @param faction the faction
+ * @return thr `AmmoBoxDefinition` for the rifle's ammo
+ * @see `GlobalDefinitions.HeavyRifle`
+ */
+ def HeavyRifleAPAmmo(faction : PlanetSideEmpire.Value) : AmmoBoxDefinition = {
+ faction match {
+ case PlanetSideEmpire.TR => bullet_9mm_AP
+ case PlanetSideEmpire.NC => shotgun_shell_AP
+ case PlanetSideEmpire.VS => energy_cell
+ case PlanetSideEmpire.NEUTRAL => bullet_9mm_AP
+ }
+ }
+
/**
* For a given faction, provide the anti-vehicular launcher.
* @param faction the faction
@@ -1268,13 +1313,13 @@ object GlobalDefinitions {
def MAXArms(subtype : Int, faction : PlanetSideEmpire.Value) : ToolDefinition = {
if(subtype == 1) {
- AIMAX(faction)
+ AI_MAX(faction)
}
else if(subtype == 2) {
- AVMAX(faction)
+ AV_MAX(faction)
}
else if(subtype == 3) {
- AAMAX(faction)
+ AA_MAX(faction)
}
else {
suppressor //there are no common pool MAX arms
@@ -1292,7 +1337,7 @@ object GlobalDefinitions {
}
}
- def AIMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
+ def AI_MAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
faction match {
case PlanetSideEmpire.TR => trhev_dualcycler
case PlanetSideEmpire.NC => nchev_scattercannon
@@ -1301,7 +1346,16 @@ object GlobalDefinitions {
}
}
- def AVMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
+ def AI_MAXAmmo(faction : PlanetSideEmpire.Value) : AmmoBoxDefinition = {
+ faction match {
+ case PlanetSideEmpire.TR => dualcycler_ammo
+ case PlanetSideEmpire.NC => scattercannon_ammo
+ case PlanetSideEmpire.VS => quasar_ammo
+ case PlanetSideEmpire.NEUTRAL => bullet_9mm //there are no common pool MAX arms
+ }
+ }
+
+ def AV_MAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
faction match {
case PlanetSideEmpire.TR => trhev_pounder
case PlanetSideEmpire.NC => nchev_falcon
@@ -1310,7 +1364,16 @@ object GlobalDefinitions {
}
}
- def AAMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
+ def AV_MAXAmmo(faction : PlanetSideEmpire.Value) : AmmoBoxDefinition = {
+ faction match {
+ case PlanetSideEmpire.TR => pounder_ammo
+ case PlanetSideEmpire.NC => falcon_ammo
+ case PlanetSideEmpire.VS => comet_ammo
+ case PlanetSideEmpire.NEUTRAL => bullet_9mm //there are no common pool MAX arms
+ }
+ }
+
+ def AA_MAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
faction match {
case PlanetSideEmpire.TR => trhev_burster
case PlanetSideEmpire.NC => nchev_sparrow
@@ -1319,6 +1382,15 @@ object GlobalDefinitions {
}
}
+ def AA_MAXAmmo(faction : PlanetSideEmpire.Value) : AmmoBoxDefinition = {
+ faction match {
+ case PlanetSideEmpire.TR => burster_ammo
+ case PlanetSideEmpire.NC => sparrow_ammo
+ case PlanetSideEmpire.VS => starfire_ammo
+ case PlanetSideEmpire.NEUTRAL => bullet_9mm //there are no common pool MAX arms
+ }
+ }
+
def PortableMannedTurret(faction :PlanetSideEmpire.Value) : TurretDeployableDefinition = {
faction match {
case PlanetSideEmpire.TR => portable_manned_turret_tr
@@ -1336,7 +1408,7 @@ object GlobalDefinitions {
*/
def isGrenade(edef : EquipmentDefinition) : Boolean = {
edef match {
- case `frag_grenade` | `jammer_grenade` | `plasma_grenade` =>
+ case `frag_grenade` | `jammer_grenade` | `plasma_grenade` | `dynomite` =>
true
case _ =>
false
@@ -1651,6 +1723,18 @@ object GlobalDefinitions {
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
@@ -4079,6 +4163,47 @@ object GlobalDefinitions {
flamethrower.FireModes(1).Rounds = 50
flamethrower.Tile = InventoryTile.Tile63
+ winchester.Name = "winchester"
+ winchester.Size = EquipmentSize.Rifle
+ winchester.AmmoTypes += winchester_ammo
+ winchester.ProjectileTypes += winchester_projectile
+ winchester.FireModes += new FireModeDefinition
+ winchester.FireModes.head.AmmoTypeIndices += 0
+ winchester.FireModes.head.AmmoSlotIndex = 0
+ winchester.FireModes.head.Magazine = 1
+ winchester.Tile = InventoryTile.Tile93
+
+ pellet_gun.Name = "pellet_gun"
+ pellet_gun.Size = EquipmentSize.Rifle
+ pellet_gun.AmmoTypes += pellet_gun_ammo
+ pellet_gun.ProjectileTypes += pellet_gun_projectile
+ pellet_gun.FireModes += new PelletFireModeDefinition
+ pellet_gun.FireModes.head.AmmoTypeIndices += 0
+ pellet_gun.FireModes.head.AmmoSlotIndex = 0
+ pellet_gun.FireModes.head.Magazine = 1
+ pellet_gun.FireModes.head.Chamber = 8 //1 shells * 8 pellets = 8
+ pellet_gun.Tile = InventoryTile.Tile63
+
+ six_shooter.Name = "six_shooter"
+ six_shooter.Size = EquipmentSize.Pistol
+ six_shooter.AmmoTypes += six_shooter_ammo
+ six_shooter.ProjectileTypes += six_shooter_projectile
+ six_shooter.FireModes += new FireModeDefinition
+ six_shooter.FireModes.head.AmmoTypeIndices += 0
+ six_shooter.FireModes.head.AmmoSlotIndex = 0
+ six_shooter.FireModes.head.Magazine = 6
+ six_shooter.Tile = InventoryTile.Tile33
+
+ dynomite.Name = "dynomite"
+ dynomite.Size = EquipmentSize.Pistol
+ dynomite.AmmoTypes += frag_grenade_ammo
+ dynomite.ProjectileTypes += dynomite_projectile
+ dynomite.FireModes += new FireModeDefinition
+ dynomite.FireModes.head.AmmoTypeIndices += 0
+ dynomite.FireModes.head.AmmoSlotIndex = 0
+ dynomite.FireModes.head.Magazine = 1
+ dynomite.Tile = InventoryTile.Tile22
+
trhev_dualcycler.Name = "trhev_dualcycler"
trhev_dualcycler.Size = EquipmentSize.Max
trhev_dualcycler.AmmoTypes += dualcycler_ammo
diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index 250c81b2..09e261c2 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -48,9 +48,14 @@ class Player(private val core : Avatar) extends PlanetSideGameObject
private var continent : String = "home2" //the zone id
- //SouNourS things
- /** Last medkituse. */
- var lastMedkit : Long = 0
+ var silenced : Boolean = false
+ var firstLoad : Boolean = false
+ def FirstLoad : Boolean = firstLoad
+ def FirstLoad_=(status : Boolean) : Boolean = {
+ firstLoad = status
+ FirstLoad
+ }
+ var death_by : Int = 0
var lastSeenStreamMessage : Array[Long] = Array.fill[Long](65535)(0L)
var lastShotSeq_time : Int = -1
/** From PlanetsideAttributeMessage */
diff --git a/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
index cb054d4e..e2a34fc2 100644
--- a/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
+++ b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala
@@ -7,6 +7,7 @@ import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3}
final case class PlayerSource(name : String,
+ char_id : Long,
obj_def : ObjectDefinition,
faction : PlanetSideEmpire.Value,
exosuit : ExoSuitType.Value,
@@ -19,6 +20,7 @@ final case class PlayerSource(name : String,
modifiers : ResistanceProfile) extends SourceEntry {
override def Name = name
override def Faction = faction
+ override def CharId = char_id
def Definition = obj_def
def ExoSuit = exosuit
def Seated = seated
@@ -32,7 +34,7 @@ final case class PlayerSource(name : String,
object PlayerSource {
def apply(tplayer : Player) : PlayerSource = {
- PlayerSource(tplayer.Name, tplayer.Definition, tplayer.Faction, tplayer.ExoSuit, tplayer.VehicleSeated.nonEmpty,
+ PlayerSource(tplayer.Name, tplayer.CharId, tplayer.Definition, tplayer.Faction, tplayer.ExoSuit, tplayer.VehicleSeated.nonEmpty,
tplayer.Health, tplayer.Armor, tplayer.Position, tplayer.Orientation, tplayer.Velocity, tplayer.asInstanceOf[ResistanceProfile])
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala
index c8593fc7..5b7bd202 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala
@@ -155,6 +155,7 @@ object Building {
def Structure(buildingType : StructureType.Value, location : Vector3)(guid : Int, map_id : Int, zone : Zone, context : ActorContext) : Building = {
import akka.actor.Props
+
val obj = new Building(guid, map_id, zone, buildingType, GlobalDefinitions.building)
obj.Position = location
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$map_id-$buildingType-building")
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
index 2f1d56c7..b1b66624 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/EquipmentTerminalDefinition.scala
@@ -66,7 +66,10 @@ object EquipmentTerminalDefinition {
"lancer_cartridge" -> MakeAmmoBox(lancer_cartridge),
"bolt" -> MakeAmmoBox(bolt),
"oicw_ammo" -> MakeAmmoBox(oicw_ammo), //scorpion missile
- "flamethrower_ammo" -> MakeAmmoBox(flamethrower_ammo)
+ "flamethrower_ammo" -> MakeAmmoBox(flamethrower_ammo),
+ "winchester_ammo" -> MakeAmmoBox(winchester_ammo),
+ "pellet_gun_ammo" -> MakeAmmoBox(pellet_gun_ammo),
+ "six_shooter_ammo" -> MakeAmmoBox(six_shooter_ammo)
)
val maxAmmo : Map[String, () => Equipment] = Map(
"dualcycler_ammo" -> MakeAmmoBox(dualcycler_ammo),
@@ -172,7 +175,11 @@ object EquipmentTerminalDefinition {
"heavy_sniper" -> MakeTool(heavy_sniper), //hsr
"bolt_driver" -> MakeTool(bolt_driver),
"oicw" -> MakeTool(oicw), //scorpion
- "flamethrower" -> MakeTool(flamethrower)
+ "flamethrower" -> MakeTool(flamethrower),
+ "winchester" -> MakeTool(winchester),
+ "pellet_gun" -> MakeTool(pellet_gun),
+ "six_shooter" -> MakeTool(six_shooter),
+ "dynomite" -> MakeTool(dynomite)
)
/**
* A `Map` of operations for producing the `Tool` `Equipment` for utilities.
diff --git a/common/src/main/scala/net/psforever/objects/vital/Vitality.scala b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala
index f5945790..e15c696f 100644
--- a/common/src/main/scala/net/psforever/objects/vital/Vitality.scala
+++ b/common/src/main/scala/net/psforever/objects/vital/Vitality.scala
@@ -26,6 +26,8 @@ final case class HealFromImplant(target : PlayerSource, amount : Int, implant :
final case class HealFromExoSuitChange(target : PlayerSource, exosuit : ExoSuitType.Value) extends HealingActivity(target)
+final case class RepairFromKit(target : PlayerSource, amount : Int, kit_def : KitDefinition) extends HealingActivity(target)
+
final case class RepairFromTerm(target : VehicleSource, amount : Int, term_def : TerminalDefinition) extends HealingActivity(target)
final case class VehicleShieldCharge(target : VehicleSource, amount : Int) extends HealingActivity(target) //TODO facility
diff --git a/common/src/main/scala/net/psforever/packet/game/LoginRespMessage.scala b/common/src/main/scala/net/psforever/packet/game/LoginRespMessage.scala
index 0d17aefa..7936a407 100644
--- a/common/src/main/scala/net/psforever/packet/game/LoginRespMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/LoginRespMessage.scala
@@ -44,6 +44,7 @@ object LoginRespMessage extends Marshallable[LoginRespMessage] {
object LoginError extends Enumeration {
type Type = Value
val Success = Value(0)
+ val unk1 = Value(1)
val BadUsernameOrPassword = Value(5)
val BadVersion = Value(0xf)
@@ -53,18 +54,19 @@ object LoginRespMessage extends Marshallable[LoginRespMessage] {
object StationError extends Enumeration {
type Type = Value
val AccountActive = Value(1)
- val AccountClosed = Value(2)
+ val AccountClosed = Value(2) // "Your Station account is currently closed"
implicit val codec = PacketHelpers.createLongEnumerationCodec(this, uint32L)
}
object StationSubscriptionStatus extends Enumeration {
type Type = Value
- val None = Value(1)
- val Active = Value(2) /// Not sure about this one (guessing)
- val Closed = Value(4)
- val Trial = Value(5) /// Not sure about this one either
- val TrialExpired = Value(6)
+ val None = Value(1) // "You do not have a PlanetSide subscription"
+ val Active = Value(2) /// Not sure about this one (guessing) (no ingame error message)
+ val unk3 = Value(3)
+ val Closed = Value(4) // "Your PlanetSide subscription is currently closed"
+ val Trial = Value(5) /// Not sure about this one either (no ingame error message)
+ val TrialExpired = Value(6) // "Your trial PlanetSide subscription has expired"
implicit val codec = PacketHelpers.createLongEnumerationCodec(this, uint32L)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
index 4c33eab1..e5aae35a 100644
--- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala
@@ -126,11 +126,12 @@ import scodec.codecs._
* - OLD: "Info under avatar name : 0 = Looking For Squad Members, 1 = LFS`"
* `35 - BR. Value is the BR`
* `36 - CR. Value is the CR`
+ * `38 - Spawn active or not. MUST use base MapId not base GUID`
* `43 - Info on avatar name : 0 = Nothing, 1 = "(LD)" message`
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`
* `46 - Sends "Generator damage is at a critical level!" message`
- * `47 - Sets base NTU level to CRITICAL. MUST use base modelId not base GUID`
- * `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base modelId not base GUID`
+ * `47 - Sets base NTU level to CRITICAL. MUST use base MapId not base GUID`
+ * `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base MapId not base GUID`
* `49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)`
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)`
* `53 - LFS. Value is 1 to flag LFS`
@@ -143,7 +144,7 @@ import scodec.codecs._
* -- e.g., 13 = 8 + 4 + 1 = fire and LLU and plasma
* `55 - "Someone is attempting to Heal you". Value is 1`
* `56 - "Someone is attempting to Repair you". Value is 1`
- * `67 - Enables base shields (from cavern module/lock). MUST use base modelId not GUID`
+ * `67 - Enables base shields (from cavern module/lock). MUST use base MapId not GUID`
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`
* `77 - Cavern Facility Captures. Value is the number of captures`
* `78 - Cavern Kills. Value is the number of kills`
diff --git a/common/src/main/scala/net/psforever/packet/game/VNLWorldStatusMessage.scala b/common/src/main/scala/net/psforever/packet/game/VNLWorldStatusMessage.scala
index 6f34382c..75ac8289 100644
--- a/common/src/main/scala/net/psforever/packet/game/VNLWorldStatusMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/VNLWorldStatusMessage.scala
@@ -18,7 +18,7 @@ object WorldStatus extends Enumeration {
// this enumeration starts from one and is subtracted from before processing (0x005FF12A)
object ServerType extends Enumeration(1) {
type Type = Value
- val Development, Beta, Released = Value
+ val Development, Beta, Released, Released_Gemini = Value
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
index 776d367c..527fdc5d 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
@@ -392,6 +392,8 @@ object ObjectClass {
final val order_terminal = 612
final val order_terminala = 613
final val order_terminalb = 614
+ final val portable_ammo_terminal = 684
+ final val portable_order_terminal = 690
final val targeting_laser_dispenser = 851
final val teleportpad_terminal = 853
diff --git a/common/src/main/scala/net/psforever/types/BailType.scala b/common/src/main/scala/net/psforever/types/BailType.scala
index a2bff67f..3290c5a4 100644
--- a/common/src/main/scala/net/psforever/types/BailType.scala
+++ b/common/src/main/scala/net/psforever/types/BailType.scala
@@ -8,7 +8,13 @@ object BailType extends Enumeration {
type Type = Value
val Normal = Value(0)
+ val Unk1 = Value(1) // to have Xtoolspar working
+ val Unk2 = Value(2) // to have Xtoolspar working
+ val Unk3 = Value(3) // to have Xtoolspar working
val Kicked = Value(4) // User was kicked out by vehicle owner or locked from vehicle
+ val Unk5 = Value(5) // to have Xtoolspar working
+ val Unk6 = Value(6) // to have Xtoolspar working
+ val Unk7 = Value(7) // to have Xtoolspar working
val Bailed = Value(8) // User bailed out
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
diff --git a/common/src/main/scala/net/psforever/types/CargoStatus.scala b/common/src/main/scala/net/psforever/types/CargoStatus.scala
index 9cee94a7..691e8793 100644
--- a/common/src/main/scala/net/psforever/types/CargoStatus.scala
+++ b/common/src/main/scala/net/psforever/types/CargoStatus.scala
@@ -9,6 +9,7 @@ object CargoStatus extends Enumeration {
val Empty = Value(0)
val InProgress = Value(1)
+ val UNK1 = Value(2) // to have Xtoolspar working
val Occupied = Value(3)
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
diff --git a/common/src/main/scala/net/psforever/types/DriveState.scala b/common/src/main/scala/net/psforever/types/DriveState.scala
index 07109a68..dd0f8ccc 100644
--- a/common/src/main/scala/net/psforever/types/DriveState.scala
+++ b/common/src/main/scala/net/psforever/types/DriveState.scala
@@ -15,6 +15,9 @@ object DriveState extends Enumeration {
val Undeploying = Value(1)
val Deploying = Value(2)
val Deployed = Value(3)
+ val UNK4 = Value(4) // to have Xtoolspar working
+ val UNK5 = Value(5) // to have Xtoolspar working
+ val UNK6 = Value(6) // to have Xtoolspar working
val State7 = Value(7) //unknown; not encountered on a vehicle that can deploy; functions like Mobile
val State127 = Value(127) //unknown
}
diff --git a/common/src/main/scala/net/psforever/types/MeritCommendation.scala b/common/src/main/scala/net/psforever/types/MeritCommendation.scala
index 45f70efc..03a430e0 100644
--- a/common/src/main/scala/net/psforever/types/MeritCommendation.scala
+++ b/common/src/main/scala/net/psforever/types/MeritCommendation.scala
@@ -507,6 +507,9 @@ object MeritCommendation extends Enumeration {
if(n > Int.MaxValue) {
Attempt.failure(Err(s"value $n is too high, above maximum integer value ${Int.MaxValue}"))
}
+ else if(n > 429) { // TODO remove that. It's for use Xtoolspar.
+ Attempt.failure(Err(s"value $n should not exist"))
+ }
else {
Attempt.successful(MeritCommendation(n.toInt))
}
diff --git a/common/src/main/scala/services/avatar/AvatarAction.scala b/common/src/main/scala/services/avatar/AvatarAction.scala
index 63135825..c9042cbe 100644
--- a/common/src/main/scala/services/avatar/AvatarAction.scala
+++ b/common/src/main/scala/services/avatar/AvatarAction.scala
@@ -36,6 +36,7 @@ object AvatarAction {
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
+ final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
final case class PutDownFDU(player_guid : PlanetSideGUID) extends Action
diff --git a/common/src/main/scala/services/avatar/AvatarResponse.scala b/common/src/main/scala/services/avatar/AvatarResponse.scala
index 54df469e..f7f1a3b3 100644
--- a/common/src/main/scala/services/avatar/AvatarResponse.scala
+++ b/common/src/main/scala/services/avatar/AvatarResponse.scala
@@ -30,6 +30,7 @@ object AvatarResponse {
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
final case class ObjectHeld(slot : Int) extends Response
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
+ final case class PlanetsideAttributeSelf(attribute_type : Int, attribute_value : Long) extends Response
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
final case class PutDownFDU(target_guid : PlanetSideGUID) extends Response
final case class Release(player : Player) extends Response
diff --git a/common/src/main/scala/services/avatar/AvatarService.scala b/common/src/main/scala/services/avatar/AvatarService.scala
index 8f942218..18f5c7c0 100644
--- a/common/src/main/scala/services/avatar/AvatarService.scala
+++ b/common/src/main/scala/services/avatar/AvatarService.scala
@@ -139,6 +139,10 @@ class AvatarService extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlanetsideAttribute(attribute_type, attribute_value))
)
+ case AvatarAction.PlanetsideAttributeSelf(guid, attribute_type, attribute_value) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlanetsideAttributeSelf(attribute_type, attribute_value))
+ )
case AvatarAction.PlayerState(guid, msg, spectator, weapon) =>
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
diff --git a/common/src/main/scala/services/chat/ChatAction.scala b/common/src/main/scala/services/chat/ChatAction.scala
new file mode 100644
index 00000000..f1077381
--- /dev/null
+++ b/common/src/main/scala/services/chat/ChatAction.scala
@@ -0,0 +1,18 @@
+// Copyright (c) 2017 PSForever
+package services.chat
+
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.{ChatMsg, PlanetSideGUID}
+import net.psforever.types.{PlanetSideEmpire, Vector3}
+
+object ChatAction {
+ sealed trait Action
+
+ final case class Local(player_guid : PlanetSideGUID, player_name : String, continent : Zone, player_pos : Vector3, player_faction : PlanetSideEmpire.Value, msg : ChatMsg) extends Action
+ final case class Tell(player_guid : PlanetSideGUID, player_name : String, msg : ChatMsg) extends Action
+ final case class Broadcast(player_guid : PlanetSideGUID, player_name : String, continent : Zone, player_pos : Vector3, player_faction : PlanetSideEmpire.Value, msg : ChatMsg) extends Action
+ final case class Voice(player_guid : PlanetSideGUID, player_name : String, continent : Zone, player_pos : Vector3, player_faction : PlanetSideEmpire.Value, msg : ChatMsg) extends Action
+ final case class Note(player_guid : PlanetSideGUID, player_name : String, msg : ChatMsg) extends Action
+ final case class Squad(player_guid : PlanetSideGUID, player_name : String, continent : Zone, player_pos : Vector3, player_faction : PlanetSideEmpire.Value, msg : ChatMsg) extends Action
+ final case class GM(player_guid : PlanetSideGUID, player_name : String, msg : ChatMsg) extends Action
+}
\ No newline at end of file
diff --git a/common/src/main/scala/services/chat/ChatResponse.scala b/common/src/main/scala/services/chat/ChatResponse.scala
new file mode 100644
index 00000000..1d7e7e52
--- /dev/null
+++ b/common/src/main/scala/services/chat/ChatResponse.scala
@@ -0,0 +1,19 @@
+// Copyright (c) 2017 PSForever
+package services.chat
+
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.ChatMessageType
+
+object ChatResponse {
+ sealed trait Response
+
+ final case class Local(sender : String, messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String]) extends Response
+ final case class Tell(sender : String, messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String]) extends Response
+ final case class UTell(sender : String, messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String]) extends Response
+ final case class Broadcast(messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String]) extends Response
+ final case class Voice(messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String]) extends Response
+ final case class Unk45(sender : String, messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String]) extends Response
+ final case class Squad(sender : String, messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String]) extends Response
+
+ final case class Text(toChannel : String, avatar_guid : PlanetSideGUID, personal : Int, messageType : ChatMessageType.Value, wideContents : Boolean, recipient : String, contents : String, note : Option[String])
+}
\ No newline at end of file
diff --git a/common/src/main/scala/services/chat/ChatService.scala b/common/src/main/scala/services/chat/ChatService.scala
new file mode 100644
index 00000000..085792dc
--- /dev/null
+++ b/common/src/main/scala/services/chat/ChatService.scala
@@ -0,0 +1,113 @@
+// Copyright (c) 2017 PSForever
+package services.chat
+
+import akka.actor.Actor
+import net.psforever.objects.LivePlayerList
+import net.psforever.packet.game.{ChatMsg, PlanetSideGUID}
+import net.psforever.types.ChatMessageType
+import services.{GenericEventBus, Service}
+
+class ChatService extends Actor {
+ private [this] val log = org.log4s.getLogger
+
+ override def preStart = {
+ log.info("Starting....")
+ }
+
+ val ChatEvents = new GenericEventBus[ChatServiceResponse]
+
+ def receive = {
+ case Service.Join(channel) =>
+ val path = s"/Chat/$channel"
+ val who = sender()
+ log.info(s"$who has joined $path")
+ ChatEvents.subscribe(who, path)
+ case Service.Leave(None) =>
+ ChatEvents.unsubscribe(sender())
+ case Service.Leave(Some(channel)) =>
+ val path = s"/Chat/$channel"
+ val who = sender()
+ log.info(s"$who has left $path")
+ ChatEvents.unsubscribe(who, path)
+ case Service.LeaveAll() =>
+ ChatEvents.unsubscribe(sender())
+
+ case ChatServiceMessage(forChannel, action) =>
+ action match {
+ case ChatAction.Local(player_guid, player_name, cont, player_pos, player_faction, msg) => // local
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, cont, player_pos, player_faction, 2, ChatMsg(ChatMessageType.CMT_OPEN,msg.wideContents,player_name,msg.contents,None))
+ )
+ case ChatAction.Tell(player_guid, player_name, msg) => // tell
+ var good : Boolean = false
+ LivePlayerList.WorldPopulation(_ => true).foreach(char => {
+ if (char.name.equalsIgnoreCase(msg.recipient)) {
+ good = true
+ }
+ })
+ if(good) {
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, target = 0, replyMessage = ChatMsg(ChatMessageType.CMT_TELL,msg.wideContents,msg.recipient,msg.contents,None))
+ )
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, target = 1, replyMessage = ChatMsg(ChatMessageType.U_CMT_TELLFROM,msg.wideContents,msg.recipient,msg.contents,None))
+ )
+ } else {
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, target = 1, replyMessage = ChatMsg(ChatMessageType.U_CMT_TELLFROM,msg.wideContents,msg.recipient,msg.contents,None))
+ )
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, target = 1, replyMessage = ChatMsg(ChatMessageType.UNK_45,msg.wideContents,"","@NoTell_Target",None))
+ )
+ }
+ case ChatAction.Broadcast(player_guid, player_name, cont, player_pos, player_faction, msg) => // broadcast
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, cont, player_pos, player_faction, 2, ChatMsg(msg.messageType,msg.wideContents,player_name,msg.contents,None))
+ )
+ case ChatAction.Voice(player_guid, player_name, cont, player_pos, player_faction, msg) => // voice
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, cont, player_pos, player_faction, 2, ChatMsg(ChatMessageType.CMT_VOICE,false,player_name,msg.contents,None))
+ )
+
+ case ChatAction.Note(player_guid, player_name, msg) => // note
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, target = 1, replyMessage = ChatMsg(ChatMessageType.U_CMT_GMTELLFROM,true, msg.recipient,msg.contents,None))
+ )
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, target = 1, replyMessage = ChatMsg(ChatMessageType.CMT_GMTELL,true,"Server","Why do you try to /note ? That's a GM command ! ... Or not, nobody can /note",None))
+ )
+ case ChatAction.Squad(player_guid, player_name, cont, player_pos, player_faction, msg) => // squad
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, cont, player_pos, player_faction, 2, ChatMsg(ChatMessageType.CMT_SQUAD,msg.wideContents,player_name,msg.contents,None))
+ )
+ case ChatAction.GM(player_guid, player_name, msg) => // GM
+ msg.messageType match {
+ case ChatMessageType.CMT_SILENCE =>
+ ChatEvents.publish(
+ ChatServiceResponse(s"/Chat/$forChannel", player_guid, msg.contents, target = 0, replyMessage = ChatMsg(ChatMessageType.CMT_SILENCE, true, "", "", None))
+ )
+// if(player_guid != PlanetSideGUID(0)) {
+//
+// val args = msg.contents.split(" ")
+// var silence_name : String = ""
+// var silence_time : Int = 5
+// if (args.length == 1) {
+// silence_name = args(0)
+// }
+// else if (args.length == 2) {
+// silence_name = args(0)
+// silence_time = args(1).toInt
+// }
+// ChatEvents.publish(
+// ChatServiceResponse(s"/Chat/$forChannel", player_guid, player_name, target = 1, replyMessage = ChatMsg(ChatMessageType.UNK_45, true, "", silence_name + " silenced for " + silence_time + " min(s)", None))
+// )
+// }
+ case _ => ;
+ }
+ case _ => ;
+ }
+
+ case msg =>
+ log.info(s"Unhandled message $msg from $sender")
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/scala/services/chat/ChatServiceMessage.scala b/common/src/main/scala/services/chat/ChatServiceMessage.scala
new file mode 100644
index 00000000..7cb944a4
--- /dev/null
+++ b/common/src/main/scala/services/chat/ChatServiceMessage.scala
@@ -0,0 +1,4 @@
+// Copyright (c) 2017 PSForever
+package services.chat
+
+final case class ChatServiceMessage(forChannel : String, actionMessage : ChatAction.Action)
\ No newline at end of file
diff --git a/common/src/main/scala/services/chat/ChatServiceResponse.scala b/common/src/main/scala/services/chat/ChatServiceResponse.scala
new file mode 100644
index 00000000..bfdb0016
--- /dev/null
+++ b/common/src/main/scala/services/chat/ChatServiceResponse.scala
@@ -0,0 +1,18 @@
+// Copyright (c) 2017 PSForever
+package services.chat
+
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.game.{ChatMsg, PlanetSideGUID}
+import net.psforever.types.{PlanetSideEmpire, Vector3}
+import services.GenericEventBusMsg
+
+final case class ChatServiceResponse(toChannel : String,
+ avatar_guid : PlanetSideGUID,
+ avatar_name : String,
+ cont : Zone = Zone.Nowhere,
+ avatar_pos : Vector3 = Vector3(0f,0f,0f),
+ avatar_faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL,
+
+ target : Int,
+ replyMessage : ChatMsg
+ ) extends GenericEventBusMsg
\ No newline at end of file
diff --git a/pslogin/src/main/scala/CryptoSessionActor.scala b/pslogin/src/main/scala/CryptoSessionActor.scala
index db3f9874..4aadc1ee 100644
--- a/pslogin/src/main/scala/CryptoSessionActor.scala
+++ b/pslogin/src/main/scala/CryptoSessionActor.scala
@@ -80,6 +80,7 @@ class CryptoSessionActor extends Actor with MDCContextAware {
clientNonce = nonce
serverNonce = Math.abs(random.nextInt())
sendResponse(PacketCoding.CreateControlPacket(ServerStart(nonce, serverNonce)))
+ log.trace(s"ClientStart($nonce), $serverNonce")
context.become(CryptoExchange)
case _ =>
diff --git a/pslogin/src/main/scala/PacketCodingActor.scala b/pslogin/src/main/scala/PacketCodingActor.scala
index 96e91a9b..ad855a5d 100644
--- a/pslogin/src/main/scala/PacketCodingActor.scala
+++ b/pslogin/src/main/scala/PacketCodingActor.scala
@@ -55,7 +55,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
private var relatedABufferTimeout : Cancellable = DefaultCancellable.obj
def AddSlottedPacketToLog(subslot: Int, packet : ByteVector): Unit = {
- val log_limit = 100 // Number of SlottedMetaPackets to keep in history
+ val log_limit = 500 // Number of SlottedMetaPackets to keep in history
if(slottedPacketLog.size > log_limit) {
slottedPacketLog = slottedPacketLog.drop(slottedPacketLog.size - log_limit)
}
@@ -326,7 +326,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
case Successful(packet) =>
handlePacketContainer(packet)
case Failure(ex) =>
- log.info(s"Failed to unmarshal $description: $ex")
+ log.info(s"Failed to unmarshal $description: $ex. Data : $data")
}
}
@@ -372,7 +372,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
packets.foreach { UnmarshalInnerPacket(_, "the inner packet of a MultiPacketEx") }
case RelatedA(slot, subslot) =>
- log.trace(s"Client indicated a packet is missing prior to slot: $slot subslot: $subslot")
+ log.trace(s"Client indicated a packet is missing prior to slot: $slot subslot: $subslot, session: ${sessionId}")
relatedALog += subslot
@@ -382,14 +382,16 @@ class PacketCodingActor extends Actor with MDCContextAware {
relatedABufferTimeout = context.system.scheduler.scheduleOnce(100 milliseconds, self, PacketCodingActor.SubslotResend())
case RelatedB(slot, subslot) =>
- log.trace(s"result $slot: subslot $subslot accepted")
+ log.trace(s"result $slot: subslot $subslot accepted, session: ${sessionId}")
// The client has indicated it's received up to a certain subslot, that means we can purge the log of any subslots prior to and including the confirmed subslot
// Find where this subslot is stored in the packet log (if at all) and drop anything to the left of it, including itself
- val pos = slottedPacketLog.keySet.toArray.indexOf(subslot)
- if(pos != -1) {
- slottedPacketLog = slottedPacketLog.drop(pos+1)
- log.trace(s"Subslots left in log: ${slottedPacketLog.keySet.toString()}")
+ if(relatedABufferTimeout.isCancelled || relatedABufferTimeout == DefaultCancellable.obj) {
+ val pos = slottedPacketLog.keySet.toArray.indexOf(subslot)
+ if(pos != -1) {
+ slottedPacketLog = slottedPacketLog.drop(pos+1)
+ log.trace(s"Subslots left in log: ${slottedPacketLog.keySet.toString()}")
+ }
}
case _ =>
sendResponseRight(container)
diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala
index cfc0c164..82585df9 100644
--- a/pslogin/src/main/scala/PsLogin.scala
+++ b/pslogin/src/main/scala/PsLogin.scala
@@ -20,6 +20,7 @@ import org.fusesource.jansi.Ansi._
import org.fusesource.jansi.Ansi.Color._
import services.ServiceManager
import services.avatar._
+import services.chat.ChatService
import services.galaxy.GalaxyService
import services.local._
import services.teamwork.SquadService
@@ -257,6 +258,7 @@ object PsLogin {
serviceManager ! ServiceManager.Register(RandomPool(50).props(Props[TaskResolver]), "taskResolver")
serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar")
serviceManager ! ServiceManager.Register(Props[LocalService], "local")
+ serviceManager ! ServiceManager.Register(Props[ChatService], "chat")
serviceManager ! ServiceManager.Register(Props[VehicleService], "vehicle")
serviceManager ! ServiceManager.Register(Props[GalaxyService], "galaxy")
serviceManager ! ServiceManager.Register(Props[SquadService], "squad")
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 82fc7ec8..b4237a44 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -51,6 +51,7 @@ import services.{RemoverActor, vehicle, _}
import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
import services.galaxy.{GalaxyResponse, GalaxyServiceResponse}
import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
+import services.chat._
import services.vehicle.support.TurretUpgrader
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import services.teamwork.{SquadAction => SquadServiceAction, SquadServiceMessage, SquadServiceResponse, SquadResponse, SquadService}
@@ -77,6 +78,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
var rightRef : ActorRef = ActorRef.noSender
var avatarService : ActorRef = ActorRef.noSender
var localService : ActorRef = ActorRef.noSender
+ var chatService: ActorRef = ActorRef.noSender
var vehicleService : ActorRef = ActorRef.noSender
var galaxyService : ActorRef = ActorRef.noSender
var squadService : ActorRef = ActorRef.noSender
@@ -93,12 +95,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
var speed : Float = 1.0f
var spectator : Boolean = false
var admin : Boolean = false
+ var noSpawnPointHere : Boolean = false
var usingMedicalTerminal : Option[PlanetSideGUID] = None
var controlled : Option[Int] = None
//keep track of avatar's ServerVehicleOverride state
var traveler : Traveler = null
var deadState : DeadState.Value = DeadState.Dead
+ var whenUsedLastAAMAX : Long = 0
+ var whenUsedLastAIMAX : Long = 0
+ var whenUsedLastAVMAX : Long = 0
+ var whenUsedLastMAX : Array[Long] = Array.fill[Long](4)(0L)
+ var whenUsedLastMAXName : Array[String] = Array.fill[String](4)("")
+ var whenUsedLastItem : Array[Long] = Array.fill[Long](1020)(0L)
+ var whenUsedLastItemName : Array[String] = Array.fill[String](1020)("")
var whenUsedLastKit : Long = 0
+ var whenUsedLastSMKit : Long = 0
+ var whenUsedLastSAKit : Long = 0
+ var whenUsedLastSSKit : Long = 0
val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](Projectile.RangeUID - Projectile.BaseUID)(None)
var drawDeloyableIcon : PlanetSideGameObject with Deployable => Unit = RedrawDeployableIcons
var updateSquad : () => Unit = NoSquadUpdates
@@ -134,6 +147,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
var squadUpdateCounter : Int = 0
val queuedSquadActions : Seq[() => Unit] = Seq(SquadUpdates, NoSquadUpdates, NoSquadUpdates, NoSquadUpdates)
+ var timeDL : Long = 0
+ var timeSurge : Long = 0
+
var amsSpawnPoints : List[SpawnPoint] = Nil
var clientKeepAlive : Cancellable = DefaultCancellable.obj
var progressBarUpdate : Cancellable = DefaultCancellable.obj
@@ -159,6 +175,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
respawnTimer.cancel
PlayerActionsToCancel()
localService ! Service.Leave()
+ chatService ! Service.Leave()
vehicleService ! Service.Leave()
avatarService ! Service.Leave()
galaxyService ! Service.Leave()
@@ -284,6 +301,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
context.become(Started)
ServiceManager.serviceManager ! Lookup("avatar")
ServiceManager.serviceManager ! Lookup("local")
+ ServiceManager.serviceManager ! Lookup("chat")
ServiceManager.serviceManager ! Lookup("vehicle")
ServiceManager.serviceManager ! Lookup("taskResolver")
ServiceManager.serviceManager ! Lookup("cluster")
@@ -302,6 +320,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case ServiceManager.LookupResult("local", endpoint) =>
localService = endpoint
log.info("ID: " + sessionId + " Got local service " + endpoint)
+ case ServiceManager.LookupResult("chat", endpoint) =>
+ chatService = endpoint
+ log.info("ID: " + sessionId + " Got chat service " + endpoint)
case ServiceManager.LookupResult("vehicle", endpoint) =>
vehicleService = endpoint
log.info("ID: " + sessionId + " Got vehicle service " + endpoint)
@@ -350,6 +371,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case LocalServiceResponse(toChannel, guid, reply) =>
HandleLocalServiceResponse(toChannel, guid, reply)
+ case ChatServiceResponse(toChannel, guid, avatar_name, cont, avatar_pos, avatar_faction, target, reply) =>
+ HandleChatServiceResponse(toChannel, guid, avatar_name, cont, avatar_pos, avatar_faction, target, reply)
+
case Mountable.MountMessages(tplayer, reply) =>
HandleMountMessages(tplayer, reply)
@@ -751,11 +775,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
val popTR = poplist.count(_.faction == PlanetSideEmpire.TR)
val popNC = poplist.count(_.faction == PlanetSideEmpire.NC)
val popVS = poplist.count(_.faction == PlanetSideEmpire.VS)
+
// StopBundlingPackets() is called on ClientInitializationComplete
StartBundlingPackets()
+
zone.Buildings.foreach({ case (id, building) => initBuilding(continentNumber, building.MapId, building) })
sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
- sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
+ if (continentNumber == 11) sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NC)) // "The NC have captured the NC Sanctuary."
+ else if (continentNumber == 12) sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.TR)) // "The TR have captured the TR Sanctuary."
+ else if (continentNumber == 13) sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary."
+ else sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
//CaptureFlagUpdateMessage()
//VanuModuleUpdateMessage()
//ModuleLimitsMessage()
@@ -1372,6 +1401,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value))
}
+ case AvatarResponse.PlanetsideAttributeSelf(attribute_type, attribute_value) =>
+ if (tplayer_guid == guid) {
+ sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value))
+ }
+
case AvatarResponse.PlayerState(msg, spectating, weaponInHand) =>
if(tplayer_guid != guid) {
val now = System.currentTimeMillis()
@@ -1571,6 +1605,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.trace(s"Clearing hack for ${target_guid}")
// Reset hack state for all players
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
+
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
case LocalResponse.HackCaptureTerminal(target_guid, unk1, unk2, isResecured) =>
@@ -1638,6 +1673,78 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
+ /**
+ * na
+ * @param toChannel na
+ * @param avatar_guid na
+ * @param target na
+ * @param reply na
+ */
+ def HandleChatServiceResponse(toChannel : String, avatar_guid : PlanetSideGUID, avatar_name : String, cont : Zone, avatar_pos : Vector3, avatar_faction : PlanetSideEmpire.Value, target : Int, reply : ChatMsg) : Unit = {
+ val tplayer_guid = if(player.HasGUID) player.GUID
+ else PlanetSideGUID(0)
+ target match {
+ case 0 => // for other(s) user(s)
+ if (player.GUID != avatar_guid) {
+ reply.messageType match {
+ case ChatMessageType.CMT_TELL =>
+ if (player.Name == reply.recipient) {
+ sendResponse(ChatMsg(reply.messageType, reply.wideContents, avatar_name, reply.contents, reply.note))
+ }
+ case ChatMessageType.CMT_SILENCE =>
+ val args = avatar_name.split(" ")
+ var silence_name : String = ""
+ var silence_time : Int = 5
+ if (args.length == 1) {
+ silence_name = args(0)
+ }
+ else if (args.length == 2) {
+ silence_name = args(0)
+ silence_time = args(1).toInt
+ }
+ if (player.Name == args(0)) {
+ if(!player.silenced) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_71, reply.wideContents, reply.recipient, "@silence_on", reply.note))
+ player.silenced = true
+ context.system.scheduler.scheduleOnce(silence_time minutes, chatService, ChatServiceMessage("gm", ChatAction.GM(PlanetSideGUID(0), player.Name, ChatMsg(ChatMessageType.CMT_SILENCE, true, "", player.Name, None))))
+ }
+ else {
+ sendResponse(ChatMsg(ChatMessageType.UNK_71, reply.wideContents, reply.recipient, "@silence_off", reply.note))
+ player.silenced = false
+ }
+ }
+ case _ =>
+ sendResponse(ChatMsg(reply.messageType, reply.wideContents, reply.recipient, reply.contents, reply.note))
+ }
+ }
+ case 1 => // for player
+ if (player.Name == avatar_name) {
+ if ((reply.contents.length > 1 && (reply.contents.dropRight(reply.contents.length - 1) != "!" || reply.contents.drop(1).dropRight(reply.contents.length - 2) == "!")) || reply.contents.length == 1) {
+ sendResponse(ChatMsg(reply.messageType, reply.wideContents, reply.recipient, reply.contents, reply.note))
+ }
+ }
+ case 2 => // both case
+ if ((reply.contents.length > 1 && (reply.contents.dropRight(reply.contents.length - 1) != "!" || reply.contents.drop(1).dropRight(reply.contents.length - 2) == "!")) || reply.contents.length == 1) {
+ reply.messageType match {
+ case ChatMessageType.CMT_OPEN =>
+ if (Vector3.Distance(player.Position, avatar_pos) < 25 && player.Faction == avatar_faction && player.Continent == cont.Id) {
+ sendResponse(ChatMsg(reply.messageType, reply.wideContents, reply.recipient, reply.contents, reply.note))
+ }
+ case ChatMessageType.CMT_SQUAD =>
+ if (player.Faction == avatar_faction) {
+ sendResponse(ChatMsg(reply.messageType, reply.wideContents, reply.recipient, reply.contents, reply.note))
+ }
+ case ChatMessageType.CMT_VOICE =>
+ if (Vector3.Distance(player.Position, avatar_pos) < 25 && player.Continent == cont.Id) {
+ sendResponse(ChatMsg(reply.messageType, reply.wideContents, reply.recipient, reply.contents, reply.note))
+ }
+ case _ =>
+ sendResponse(ChatMsg(reply.messageType, reply.wideContents, reply.recipient, reply.contents, reply.note))
+ }
+ }
+ }
+ }
+
/**
* na
* @param tplayer na
@@ -1747,7 +1854,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO check exo-suit permissions
val originalSuit = tplayer.ExoSuit
val originalSubtype = Loadout.DetermineSubtype(tplayer)
- if(originalSuit != exosuit || originalSubtype != subtype) {
+
+ val lTime = System.currentTimeMillis
+ var changeArmor : Boolean = true
+ if (lTime - whenUsedLastMAX(subtype) < 300000) {
+ changeArmor = false
+ }
+ if (changeArmor && exosuit.id == 2) {
+ for (i <- 1 to 3) {
+ sendResponse(AvatarVehicleTimerMessage(tplayer.GUID, whenUsedLastMAXName(i), 300, true))
+ whenUsedLastMAX(i) = lTime
+ }
+ }
+
+ if(originalSuit != exosuit || originalSubtype != subtype && changeArmor) {
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))
//prepare lists of valid objects
val beforeInventory = tplayer.Inventory.Clear()
@@ -1919,15 +2039,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) =>
log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}")
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true))
+ //sanitize exo-suit for change
+ val originalSuit = player.ExoSuit
+ val originalSubtype = Loadout.DetermineSubtype(tplayer)
//prepare lists of valid objects
val beforeFreeHand = tplayer.FreeHand.Equipment
val dropPred = DropPredicate(tplayer)
val (dropHolsters, beforeHolsters) = clearHolsters(tplayer.Holsters().iterator).partition(dropPred)
val (dropInventory, beforeInventory) = tplayer.Inventory.Clear().partition(dropPred)
tplayer.FreeHand.Equipment = None //terminal and inventory will close, so prematurely dropping should be fine
- //sanitize exo-suit for change
- val originalSuit = player.ExoSuit
- val originalSubtype = Loadout.DetermineSubtype(tplayer)
val fallbackSuit = ExoSuitType.Standard
val fallbackSubtype = 0
//a loadout with a prohibited exo-suit type will result in a fallback exo-suit type
@@ -1942,7 +2062,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
case permissions =>
tplayer.Certifications.intersect(permissions.toSet).nonEmpty
}) {
- (exosuit, subtype)
+ val lTime = System.currentTimeMillis
+ if (lTime - whenUsedLastMAX(subtype) < 300000){ // PTS v3 hack
+ (originalSuit, subtype)
+ } else {
+ if (lTime - whenUsedLastMAX(subtype) > 300000 && subtype != 0) {
+ for (i <- 1 to 3) {
+ sendResponse(AvatarVehicleTimerMessage(tplayer.GUID, whenUsedLastMAXName(i), 300, true))
+ whenUsedLastMAX(i) = lTime
+ }
+ }
+ (exosuit, subtype)
+ }
}
else {
log.warn(s"$tplayer no longer has permission to wear the exo-suit type $exosuit; will wear $fallbackSuit instead")
@@ -2221,34 +2352,43 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
continent.Map.TerminalToSpawnPad.get(msg.terminal_guid.guid) match {
case Some(pad_guid) =>
- val toFaction = tplayer.Faction
- val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad]
- vehicle.Faction = toFaction
- vehicle.Continent = continent.Id
- vehicle.Position = pad.Position
- vehicle.Orientation = pad.Orientation
- //default loadout, weapons
- val vWeapons = vehicle.Weapons
- weapons.foreach(entry => {
- val index = entry.start
- vWeapons.get(index) match {
- case Some(slot) =>
- entry.obj.Faction = toFaction
- slot.Equipment = None
- slot.Equipment = entry.obj
- case None =>
- log.warn(s"applying default loadout to $vehicle on spawn, but can not find a mounted weapon @ $index")
- }
- })
- //default loadout, trunk
- val vTrunk = vehicle.Trunk
- vTrunk.Clear()
- trunk.foreach(entry => {
- entry.obj.Faction = toFaction
- vTrunk += entry.start -> entry.obj
- })
- taskResolver ! RegisterNewVehicle(vehicle, pad)
- sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))
+ val lTime = System.currentTimeMillis
+ if (lTime - whenUsedLastItem(vehicle.Definition.ObjectId) > 300000) {
+ whenUsedLastItem(vehicle.Definition.ObjectId) = lTime
+ whenUsedLastItemName(vehicle.Definition.ObjectId) = msg.item_name
+ sendResponse(AvatarVehicleTimerMessage(tplayer.GUID, msg.item_name, 300, true))
+ val toFaction = tplayer.Faction
+ val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad]
+ vehicle.Faction = toFaction
+ vehicle.Continent = continent.Id
+ vehicle.Position = pad.Position
+ vehicle.Orientation = pad.Orientation
+ //default loadout, weapons
+ val vWeapons = vehicle.Weapons
+ weapons.foreach(entry => {
+ val index = entry.start
+ vWeapons.get(index) match {
+ case Some(slot) =>
+ entry.obj.Faction = toFaction
+ slot.Equipment = None
+ slot.Equipment = entry.obj
+ case None =>
+ log.warn(s"applying default loadout to $vehicle on spawn, but can not find a mounted weapon @ $index")
+ }
+ })
+ //default loadout, trunk
+ val vTrunk = vehicle.Trunk
+ vTrunk.Clear()
+ trunk.foreach(entry => {
+ entry.obj.Faction = toFaction
+ vTrunk += entry.start -> entry.obj
+ })
+ taskResolver ! RegisterNewVehicle(vehicle, pad)
+ sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))
+ }
+ else {
+ sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, false))
+ }
case None =>
log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it")
@@ -3090,7 +3230,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case (index, loadout : VehicleLoadout) =>
sendResponse(FavoritesMessage(LoadoutType.Vehicle, guid, index - 10, loadout.label))
}
- sendResponse(SetChatFilterMessage(ChatChannel.Local, false, ChatChannel.values.toList)) //TODO will not always be "on" like this
+ sendResponse(SetChatFilterMessage(ChatChannel.Broadcast, false, ChatChannel.values.toList)) //TODO will not always be "on" like this
deadState = DeadState.Alive
sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, tplayer.Position, player.Faction, true))
//looking for squad (members)
@@ -3125,6 +3265,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
StopBundlingPackets()
drawDeloyableIcon = DontRedrawIcons
+
//assert or transfer vehicle ownership
continent.GUID(player.VehicleOwned) match {
case Some(vehicle : Vehicle) if vehicle.OwnerName.contains(tplayer.Name) =>
@@ -3133,6 +3274,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ =>
player.VehicleOwned = None
}
+
//if driver of a vehicle, summon any passengers and cargo vehicles left behind on previous continent
GetVehicleAndSeat() match {
case (Some(vehicle), Some(0)) =>
@@ -3146,6 +3288,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
interstellarFerryTopLevelGUID = None
case _ => ;
}
+ if (noSpawnPointHere) {
+ RequestSanctuaryZoneSpawn(player, continent.Number)
+ }
}
/**
@@ -3649,10 +3794,62 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent))
+
+ chatService ! Service.Join("local")
+ chatService ! Service.Join("squad")
+ chatService ! Service.Join("voice")
+ chatService ! Service.Join("tell")
+ chatService ! Service.Join("broadcast")
+ chatService ! Service.Join("note")
+ chatService ! Service.Join("gm")
+
self ! SetCurrentAvatar(player)
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) =>
if(deadState == DeadState.Alive) {
+ if (timeDL != 0) {
+ if (System.currentTimeMillis() - timeDL > 500) {
+ player.Stamina = player.Stamina - 1
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
+ timeDL = System.currentTimeMillis()
+ }
+ }
+ if (timeSurge != 0) {
+ if (System.currentTimeMillis() - timeSurge > 500 && player.ExoSuit == ExoSuitType.Agile) {
+ player.Stamina = player.Stamina - 1
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
+ timeSurge = System.currentTimeMillis()
+ }
+ else if (System.currentTimeMillis() - timeSurge > 333 && player.ExoSuit == ExoSuitType.Reinforced) {
+ player.Stamina = player.Stamina - 1
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
+ timeSurge = System.currentTimeMillis()
+ }
+ else if (System.currentTimeMillis() - timeSurge > 1000 && ( player.ExoSuit == ExoSuitType.Infiltration || player.ExoSuit == ExoSuitType.Standard )) {
+ player.Stamina = player.Stamina - 1
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
+ timeSurge = System.currentTimeMillis()
+ }
+ }
+ if (player.Stamina == 0) {
+ if (avatar.Implants(0).Active) {
+ avatar.Implants(0).Active = false
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(0).id * 2))
+ sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid),ImplantAction.Activation,0,0))
+ timeDL = 0
+ }
+ if (avatar.Implants(1).Active) {
+ avatar.Implants(1).Active = false
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(1).id * 2))
+ sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid),ImplantAction.Activation,1,0))
+ timeSurge = 0
+ }
+ }
+ if (vel.isEmpty && player.Stamina != player.MaxStamina) {
+ player.Stamina = player.Stamina + 1
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
+ }
+
player.Position = pos
player.Velocity = vel
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
@@ -3697,6 +3894,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, msg, spectator, wepInHand))
updateSquad()
}
+ else {
+ timeDL = 0
+ timeSurge = 0
+ }
case msg @ ChildObjectStateMessage(object_guid, pitch, yaw) =>
//the majority of the following check retrieves information to determine if we are in control of the child
@@ -3885,6 +4086,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("Chat: " + msg)
}
else {
+ log.info("Chat: " + msg)
makeReply = false
}
if(messagetype == ChatMessageType.CMT_SUICIDE) {
@@ -3906,20 +4108,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(messagetype == ChatMessageType.CMT_VOICE) {
sendResponse(ChatMsg(ChatMessageType.CMT_VOICE, false, player.Name, contents, None))
}
- // TODO: handle this appropriately
- if(messagetype == ChatMessageType.CMT_QUIT) {
+
+ if(messagetype == ChatMessageType.CMT_QUIT) { // TODO: handle this appropriately
sendResponse(DropCryptoSession())
sendResponse(DropSession(sessionId, "user quit"))
}
//dev hack; consider bang-commands to complement slash-commands in future
if(trimContents.equals("!loc")) {
+ makeReply = true
echoContents = s"zone=${continent.Id} pos=${player.Position.x},${player.Position.y},${player.Position.z}; ori=${player.Orientation.x},${player.Orientation.y},${player.Orientation.z}"
log.info(echoContents)
}
+ else if (trimContents.equals("!list") && admin) {
+ sendResponse(ChatMsg(ChatMessageType.CMT_TELL, has_wide_contents, "Server",
+ "\\#8ID / Name (faction) Cont-PosX/PosY/PosZ", note_contents))
+ continent.LivePlayers.filterNot(_.GUID == player.GUID).sortBy(_.Name).foreach(char => {
+ sendResponse(ChatMsg(ChatMessageType.CMT_TELL, has_wide_contents, "Server",
+ "GUID / Name: " + char.GUID.guid + " / " + char.Name + " (" + char.Faction + ") " +
+ char.Continent + "-" + char.Position.x.toInt + "/" + char.Position.y.toInt + "/" + char.Position.z.toInt, note_contents))
+ })
+ }
else if(trimContents.equals("!ams")) {
makeReply = false
- if(deadState == DeadState.Release) { //player is on deployment screen (either dead or deconstructed)
- cluster ! Zone.Lattice.RequestSpawnPoint(continent.Number, player, 2)
+ if(player.isBackpack) { //player is on deployment screen (either dead or deconstructed)
+ if(deadState == DeadState.Release) { //player is on deployment screen (either dead or deconstructed)
+ cluster ! Zone.Lattice.RequestSpawnPoint(continent.Number, player, 2)
+ }
}
}
// TODO: Depending on messagetype, may need to prepend sender's name to contents with proper spacing
@@ -3928,6 +4142,43 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ChatMsg(messagetype, has_wide_contents, recipient, echoContents, note_contents))
}
+ if (messagetype == ChatMessageType.CMT_OPEN && !player.silenced) {
+ chatService ! ChatServiceMessage("local", ChatAction.Local(player.GUID, player.Name, continent, player.Position, player.Faction, msg))
+ }
+ else if (messagetype == ChatMessageType.CMT_VOICE) {
+ chatService ! ChatServiceMessage("voice", ChatAction.Voice(player.GUID, player.Name, continent, player.Position, player.Faction, msg))
+ }
+ else if (messagetype == ChatMessageType.CMT_TELL && !player.silenced) {
+ chatService ! ChatServiceMessage("tell", ChatAction.Tell(player.GUID, player.Name, msg))
+ }
+ else if (messagetype == ChatMessageType.CMT_BROADCAST && !player.silenced) {
+ chatService ! ChatServiceMessage("broadcast", ChatAction.Broadcast(player.GUID, player.Name, continent, player.Position, player.Faction, msg))
+ }
+ else if (messagetype == ChatMessageType.CMT_NOTE) {
+ chatService ! ChatServiceMessage("note", ChatAction.Note(player.GUID, player.Name, msg))
+ }
+ else if (messagetype == ChatMessageType.CMT_SILENCE && admin) {
+ chatService ! ChatServiceMessage("gm", ChatAction.GM(player.GUID, player.Name, msg))
+ }
+ else if (messagetype == ChatMessageType.CMT_SQUAD && !player.silenced) {
+ chatService ! ChatServiceMessage("squad", ChatAction.Squad(player.GUID, player.Name, continent, player.Position, player.Faction, msg))
+ }
+ else if (messagetype == ChatMessageType.CMT_WHO || messagetype == ChatMessageType.CMT_WHO_CSR || messagetype == ChatMessageType.CMT_WHO_CR ||
+ messagetype == ChatMessageType.CMT_WHO_PLATOONLEADERS || messagetype == ChatMessageType.CMT_WHO_SQUADLEADERS || messagetype == ChatMessageType.CMT_WHO_TEAMS) {
+ val poplist = continent.Players
+ val popTR = poplist.count(_.faction == PlanetSideEmpire.TR)
+ val popNC = poplist.count(_.faction == PlanetSideEmpire.NC)
+ val popVS = poplist.count(_.faction == PlanetSideEmpire.VS)
+ val contName = continent.Map.Name
+
+ StartBundlingPackets()
+ sendResponse(ChatMsg(ChatMessageType.CMT_WHO, true, "", "That command doesn't work for now, but : ", None))
+ sendResponse(ChatMsg(ChatMessageType.CMT_WHO, true, "", "NC online : " + popNC + " on " + contName, None))
+ sendResponse(ChatMsg(ChatMessageType.CMT_WHO, true, "", "TR online : " + popTR + " on " + contName, None))
+ sendResponse(ChatMsg(ChatMessageType.CMT_WHO, true, "", "VS online : " + popVS + " on " + contName, None))
+ StopBundlingPackets()
+ }
+
case msg @ VoiceHostRequest(unk, PlanetSideGUID(player_guid), data) =>
log.info("Player "+player_guid+" requested in-game voice chat.")
sendResponse(VoiceHostKill())
@@ -4171,6 +4422,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ AvatarJumpMessage(state) =>
//log.info("AvatarJump: " + msg)
+ player.Stamina = player.Stamina - 10
+ if(player.Stamina < 0) player.Stamina = 0
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
case msg @ ZipLineMessage(player_guid,origin_side,action,id,pos) =>
log.info("ZipLineMessage: " + msg)
@@ -4369,8 +4623,26 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.warn(s"LootItem: can not find where to put $item_guid")
}
- case msg @ AvatarImplantMessage(_, _, _, _) => //(player_guid, unk1, unk2, implant) =>
+ case msg @ AvatarImplantMessage(_, action, slot, status) => //(player_guid, unk1, unk2, implant) =>
log.info("AvatarImplantMessage: " + msg)
+ if (avatar.Implants(slot).Initialized) {
+ if(action == ImplantAction.Activation && status == 1) { // active
+ avatar.Implants(slot).Active = true
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(slot).id * 2 + 1))
+ if (avatar.Implant(slot).id == 3) {
+ timeDL = System.currentTimeMillis()
+ player.Stamina = player.Stamina - 3
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
+ }
+ if (avatar.Implant(slot).id == 9) timeSurge = System.currentTimeMillis()
+ } else if(action == ImplantAction.Activation && status == 0) { //desactive
+ avatar.Implants(slot).Active = false
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, avatar.Implant(slot).id * 2))
+ if (avatar.Implant(slot).id == 3) timeDL = 0
+ if (avatar.Implant(slot).id == 9) timeSurge = 0
+ }
+ sendResponse(AvatarImplantMessage(PlanetSideGUID(player.GUID.guid),action,slot,status))
+ }
case msg @ UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) =>
log.info("UseItem: " + msg)
@@ -4389,6 +4661,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
// A base is hacked
// The lock is hacked
// The player is on the inside of the door, determined by the lock orientation
+
lock.HackedBy.isDefined || lock.Owner.asInstanceOf[Building].CaptureConsoleIsHacked || lock.Faction == PlanetSideEmpire.NEUTRAL || playerIsOnInside
case None => !door.isOpen // If there's no linked IFF lock just open the door if it's closed.
})) {
@@ -4475,6 +4748,76 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
}
+ else if(kit.Definition == GlobalDefinitions.super_medkit) {
+ if(player.Health == player.MaxHealth) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", "@HealComplete", None))
+ }
+ else if(System.currentTimeMillis - whenUsedLastSMKit < 1200000) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", s"@TimeUntilNextUse^${1200 - (System.currentTimeMillis - whenUsedLastSMKit) / 1000}~", None))
+ }
+ else {
+ player.Find(kit) match {
+ case Some(index) =>
+ whenUsedLastSMKit = System.currentTimeMillis
+ player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet
+ sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
+ sendResponse(ObjectDeleteMessage(kit.GUID, 0))
+ taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID)
+ player.History(HealFromKit(PlayerSource(player), 100, kit.Definition))
+ player.Health = player.Health + 100
+ sendResponse(PlanetsideAttributeMessage(avatar_guid, 0, player.Health))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(avatar_guid, 0, player.Health))
+ case None =>
+ log.error(s"UseItem: anticipated a $kit, but can't find it")
+ }
+ }
+ }
+ else if(kit.Definition == GlobalDefinitions.super_armorkit) {
+ if(player.Armor == player.MaxArmor) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", "Armor at maximum - No repairing required.", None))
+ }
+ else if(System.currentTimeMillis - whenUsedLastSAKit < 1200000) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", s"@TimeUntilNextUse^${1200 - (System.currentTimeMillis - whenUsedLastSAKit) / 1000}~", None))
+ }
+ else {
+ player.Find(kit) match {
+ case Some(index) =>
+ whenUsedLastSAKit = System.currentTimeMillis
+ player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet
+ sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
+ sendResponse(ObjectDeleteMessage(kit.GUID, 0))
+ taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID)
+ player.History(RepairFromKit(PlayerSource(player), 200, kit.Definition))
+ player.Armor = player.Armor + 200
+ sendResponse(PlanetsideAttributeMessage(avatar_guid, 4, player.Armor))
+ avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(avatar_guid, 4, player.Armor))
+ case None =>
+ log.error(s"UseItem: anticipated a $kit, but can't find it")
+ }
+ }
+ }
+ else if(kit.Definition == GlobalDefinitions.super_staminakit) {
+ if(player.Stamina == player.MaxStamina) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", "Stamina at maximum - No recharge required.", None))
+ }
+ else if(System.currentTimeMillis - whenUsedLastSSKit < 1200000) {
+ sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", s"@TimeUntilNextUse^${300 - (System.currentTimeMillis - whenUsedLastSSKit) / 1200}~", None))
+ }
+ else {
+ player.Find(kit) match {
+ case Some(index) =>
+ whenUsedLastSSKit = System.currentTimeMillis
+ player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet
+ sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
+ sendResponse(ObjectDeleteMessage(kit.GUID, 0))
+ taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID)
+ player.Stamina = player.Stamina + 100
+ sendResponse(PlanetsideAttributeMessage(avatar_guid, 2, player.Stamina))
+ case None =>
+ log.error(s"UseItem: anticipated a $kit, but can't find it")
+ }
+ }
+ }
else {
log.warn(s"UseItem: $kit behavior not supported")
}
@@ -4488,6 +4831,93 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.warn(s"UseItem: anticipated a Kit $item_used_guid, but can't find it")
}
}
+ else if (itemType == 121 && unk3) {
+ FindWeapon match {
+ case Some(tool: Tool) =>
+ if (tool.Definition.ObjectId == 132) {
+ // TODO : bank ?
+ continent.GUID(object_guid) match {
+ case Some(tplayer: Player) =>
+ if (player.GUID != tplayer.GUID && Vector3.Distance(player.Position, tplayer.Position) < 5 && player.Faction == tplayer.Faction && player.Velocity.isEmpty) {
+ if (tplayer.MaxArmor - tplayer.Armor <= 15) {
+ tplayer.Armor = tplayer.MaxArmor
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+ val RepairPercent: Int = tplayer.Armor * 100 / tplayer.MaxArmor
+ sendResponse(RepairMessage(object_guid, RepairPercent))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeSelf(tplayer.GUID, 4, tplayer.Armor))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor))
+ }
+ if (tplayer.MaxArmor - tplayer.Armor > 15) {
+ tplayer.Armor += 15
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+ val RepairPercent: Int = tplayer.Armor * 100 / tplayer.MaxArmor
+ sendResponse(RepairMessage(object_guid, RepairPercent))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeSelf(tplayer.GUID, 4, tplayer.Armor))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor))
+ }
+ } else if (player.GUID == object_guid && player.Velocity.isEmpty) {
+ if (player.MaxArmor - player.Armor <= 15) {
+ player.Armor = player.MaxArmor
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+// sendResponse(RepairMessage(object_guid, player.Armor)) // Todo is that needed ?
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 4, player.Armor))
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(player.GUID, 4, player.Armor))
+ }
+ if (player.MaxArmor - player.Armor > 15) {
+ player.Armor += 15
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+// sendResponse(RepairMessage(object_guid, player.Armor)) // Todo is that needed ?
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 4, player.Armor))
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(player.GUID, 4, player.Armor))
+ }
+ }
+ case _ => ;
+ }
+ } else if (tool.Definition.ObjectId == 531) {
+ // TODO : med app ?
+ continent.GUID(object_guid) match {
+ case Some(tplayer: Player) =>
+ if (player.GUID != tplayer.GUID && Vector3.Distance(player.Position, tplayer.Position) < 5 && player.Faction == tplayer.Faction && player.Velocity.isEmpty) {
+ if (tplayer.MaxHealth - tplayer.Health <= 10) {
+ tplayer.Health = tplayer.MaxHealth
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+ val RepairPercent: Int = tplayer.Health * 100 / tplayer.MaxHealth
+ sendResponse(RepairMessage(object_guid, RepairPercent))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeSelf(tplayer.GUID, 0, tplayer.Health))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 0, tplayer.Health))
+ }
+ if (tplayer.MaxHealth - tplayer.Health > 10) {
+ tplayer.Health += 10
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+ val RepairPercent: Int = tplayer.Health * 100 / tplayer.MaxHealth
+ sendResponse(RepairMessage(object_guid, RepairPercent))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeSelf(tplayer.GUID, 0, tplayer.Health))
+ avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 0, tplayer.Health))
+ }
+ }
+ case _ => ;
+ }
+ if (player.GUID == object_guid && player.Velocity.isEmpty) {
+ if (player.MaxHealth - player.Health <= 10) {
+ player.Health = player.MaxHealth
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+// sendResponse(RepairMessage(object_guid, player.Health)) // Todo is that needed ?
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 0, player.Health))
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(player.GUID, 0, player.Health))
+ }
+ if (player.MaxHealth - player.Health > 10) {
+ player.Health += 10
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+// sendResponse(RepairMessage(object_guid, player.Health)) // Todo is that needed ?
+ sendResponse(PlanetsideAttributeMessage(player.GUID, 0, player.Health))
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(player.GUID, 0, player.Health))
+ }
+ }
+
+ }
+ case None => ;
+ }
+ }
case Some(locker : Locker) =>
if(locker.Faction != player.Faction && locker.HackedBy.isEmpty) {
@@ -4568,6 +4998,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
else if(ammo == Ammo.armor_canister && obj.Health < obj.MaxHealth) {
//repair turret
+ obj.Health += 48
+ if (obj.Health > obj.MaxHealth) obj.Health = obj.MaxHealth
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+ val RepairPercent: Int = obj.Health * 100 / obj.MaxHealth
+ sendResponse(RepairMessage(object_guid, RepairPercent))
+ avatarService ! AvatarServiceMessage(obj.Continent, AvatarAction.PlanetsideAttribute(obj.GUID, 0, obj.Health))
}
}
else if(tool.Definition == GlobalDefinitions.trek) {
@@ -4603,6 +5039,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
equipment.get.Definition match {
case GlobalDefinitions.nano_dispenser =>
//TODO repairing behavior
+ if (player.Velocity.isEmpty && Vector3.Distance(player.Position, obj.Position) < 5) {
+ if (obj.Health < obj.MaxHealth) {
+ obj.Health += 48
+ // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
+ val RepairPercent: Int = obj.Health * 100 / obj.MaxHealth
+ sendResponse(RepairMessage(object_guid, RepairPercent))
+ avatarService ! AvatarServiceMessage(obj.Continent, AvatarAction.PlanetsideAttribute(obj.GUID, 0, obj.Health))
+ }
+ }
case _ => ;
}
@@ -4943,6 +5388,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
EmptyMagazine(weapon_guid, tool)
}
else { //shooting
+ if (tool.FireModeIndex == 1 && (tool.Definition.Name == "anniversary_guna" || tool.Definition.Name == "anniversary_gun" || tool.Definition.Name == "anniversary_gunb")) {
+ player.Stamina = 0
+ avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
+ }
+
prefire = shooting.orElse(Some(weapon_guid))
tool.Discharge
val projectileIndex = projectile_guid.guid - Projectile.BaseUID
@@ -5051,8 +5501,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
})) =>
cluster ! Zone.Lattice.RequestSpecificSpawnPoint(dest_continent_guid.guid, player, dest_building_guid)
- case _ =>
- RequestSanctuaryZoneSpawn(player, continent.Number)
+ case Some(wg : WarpGate) if(!wg.Active) =>
+ log.info(s"WarpgateRequest: inactive WarpGate")
+
+ case _ =>
+ RequestSanctuaryZoneSpawn(player, continent.Number)
}
}
else {
@@ -5218,7 +5671,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
self ! DismountVehicleCargoMsg(player.GUID, vehicle.GUID, true, false, false)
}
case None => ; // No vehicle in cargo
- }
+ }
case None => ; // Not a cargo mounting point
}
@@ -6107,7 +6560,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
def UnAccessContents(vehicle : Vehicle) : Unit = {
vehicleService ! Service.Leave(Some(s"${vehicle.Actor}"))
vehicle.Trunk.Items.foreach(entry =>{
- sendResponse(ObjectDeleteMessage(entry.obj.GUID, 0))
+ sendResponse(ObjectDeleteMessage(entry.obj.GUID, 0))
})
}
@@ -6989,8 +7442,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
) = building.Info
sendResponse(
BuildingInfoUpdateMessage(
- continentNumber,
- buildingNumber,
+ building.Zone.Number,
+ building.MapId,
ntuLevel,
isHacked, empireHack, hackTimeRemaining, controllingEmpire,
unk1, unk1x,
@@ -7020,8 +7473,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
case wg : WarpGate =>
sendResponse(
BuildingInfoUpdateMessage(
- continentNumber,
- buildingNumber,
+ building.Zone.Number,
+ building.MapId,
ntu_level = 0,
is_hacked = false,
empire_hack = PlanetSideEmpire.NEUTRAL,
@@ -7320,6 +7773,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.Population ! Zone.Population.Spawn(avatar, player)
//cautious redundancy
deadState = DeadState.Alive
+
+ val lTime = System.currentTimeMillis
+ for (i <- 0 to whenUsedLastItem.length-1) {
+ if (lTime - whenUsedLastItem(i) < 300000) {
+ sendResponse(AvatarVehicleTimerMessage(player.GUID, whenUsedLastItemName(i), 300 - ((lTime - whenUsedLastItem(i)) / 1000 toInt), true))
+ }
+ }
+ for (i <- 1 to 3) {
+ if (lTime - whenUsedLastMAX(i) < 300000) {
+ sendResponse(AvatarVehicleTimerMessage(player.GUID, whenUsedLastMAXName(i), 300 - ((lTime - whenUsedLastMAX(i)) / 1000 toInt), true))
+ }
+ }
+
}
/**
@@ -7759,7 +8225,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param flight whether the vehicle is ascending or not, if the vehicle is an applicable type
*/
def ServerVehicleOverride(vehicle : Vehicle, speed : Int = 0, flight : Int = 0) : Unit = {
- controlled = Some(speed)
+ controlled = Some(speed)
sendResponse(ServerVehicleOverrideMsg(true, true, false, false, flight, 0, speed, Some(0)))
}
@@ -8083,7 +8549,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* Additional effort is exerted to ensure that the requirements for the given ammunition are satisfied.
* If no satisfactory combination is achieved, the original state will be restored.
* @param obj the `ConstructionItem` object
- * @param originalModeIndex the starting point ammunition type mode index
+ * @param originalAmmoIndex the starting point ammunition type mode index
*/
def PerformConstructionItemAmmoChange(obj : ConstructionItem, originalAmmoIndex : Int) : Unit = {
val certifications = player.Certifications
@@ -9029,8 +9495,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z)))
UseRouterTelepadEffect(pguid, sguid, dguid)
StopBundlingPackets()
-// vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(router), continent))
-// vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(router, continent, router.Definition.DeconstructionTime))
+ // vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(router), continent))
+ // vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(router, continent, router.Definition.DeconstructionTime))
localService ! LocalServiceMessage(continent.Id, LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid))
}
else {
diff --git a/pslogin/src/main/scala/csr/CSRZoneImpl.scala b/pslogin/src/main/scala/csr/CSRZoneImpl.scala
index 9fc33c3e..a509aefd 100644
--- a/pslogin/src/main/scala/csr/CSRZoneImpl.scala
+++ b/pslogin/src/main/scala/csr/CSRZoneImpl.scala
@@ -337,7 +337,7 @@ object CSRZoneImpl {
"anu" -> Vector3(3479, 2556, 56),
"bel" -> Vector3(3665, 4626, 58),
"caer" -> Vector3(4570, 2601, 56),
- "dagd" -> Vector3(5825, 4449, 55),
+ "dagda" -> Vector3(5825, 4449, 55),
"eadon" -> Vector3(2725, 2853, 53),
"gwydion" -> Vector3(5566, 3739, 61),
"lugh" -> Vector3(6083, 5069, 72),