Merge branch 'master' into projectiles

This commit is contained in:
Fate-JH 2019-11-29 11:19:17 -05:00 committed by GitHub
commit b2ec9d2cb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 323 additions and 203 deletions

View file

@ -17,9 +17,63 @@ trait WorldEntity {
def Velocity_=(vec : Option[Vector3]) : Option[Vector3]
def Velocity_=(vec : Vector3) : Option[Vector3] = Velocity = Some(vec)
/**
* A velocity of non-zero is the same as moving.
* @return `true`, if we are moving; `false`, otherwise
*/
def isMoving : Boolean = WorldEntity.isMoving(Velocity)
/**
* This object is not considered moving unless it is moving at least as fast as a certain velocity.
* @param test the velocity to test against
* @return `true`, if we are moving; `false`, otherwise
*/
def isMoving(test : Vector3) : Boolean = WorldEntity.isMoving(Velocity, test)
/**
* This object is not considered moving unless it is moving at least as fast as a certain velocity.
* @param test the (squared) velocity to test against
* @return `true`, if we are moving; `false`, otherwise
*/
def isMoving(test : Float) : Boolean = WorldEntity.isMoving(Velocity, test)
}
object WorldEntity {
/**
* A velocity of non-zero is the same as moving.
* @return `true`, if we are moving; `false`, otherwise
*/
def isMoving(velocity : Option[Vector3]) : Boolean = {
velocity match {
case None => false
case Some(Vector3.Zero) => false
case Some(_) => true
}
}
/**
* This object is not considered moving unless it is moving at least as fast as a certain velocity.
* @param velocity the optional sample velocity
* @param test the (squared) velocity to test against
* @return `true`, if we are moving; `false`, otherwise
*/
def isMoving(velocity : Option[Vector3], test : Vector3) : Boolean = WorldEntity.isMoving(velocity, Vector3.MagnitudeSquared(test))
/**
* This object is not considered moving unless it is moving at least as fast as a certain velocity.
* @param velocity the optional sample velocity
* @param test the (squared) velocity to test against
* @return `true`, if we are moving; `false`, otherwise
*/
def isMoving(velocity : Option[Vector3], test : Float) : Boolean = {
velocity match {
case None => false
case Some(Vector3.Zero) => false
case Some(v) => Vector3.MagnitudeSquared(v) >= test
}
}
def toString(obj : WorldEntity) : String = {
s"pos=${obj.Position}, ori=${obj.Orientation}"
}

View file

@ -7,7 +7,6 @@ import net.psforever.objects.entity.{Identifiable, WorldEntity}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.turret.TurretDefinition
import net.psforever.types.Vector3
object MountableBehavior {
/**
@ -79,10 +78,9 @@ object MountableBehavior {
val dismountBehavior : Receive = {
case Mountable.TryDismount(user, seat_num) =>
val obj = MountableObject
val velocity = obj.Velocity.getOrElse(Vector3.Zero)
obj.Seat(seat_num) match {
case Some(seat) =>
if(seat.Bailable || velocity == Vector3.Zero || Vector3.MagnitudeSquared(velocity).toInt == 0) {
if(seat.Bailable || !obj.isMoving) {
seat.Occupant = None
user.VehicleSeated = None
sender ! Mountable.MountMessages(user, Mountable.CanDismount(obj, seat_num))

View file

@ -51,7 +51,7 @@ object AutoDriveControls {
* @param vehicle the vehicle being controlled
* @return `true`, if the action can (probably) be accomplished under the current conditions; `false`, otherwise
*/
def Validate(vehicle : Vehicle) : Boolean = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero).xy) > 0
def Validate(vehicle : Vehicle) : Boolean = vehicle.isMoving
/**
* Perform a test to determine if the vehicle has reached a set of conditions
* where the action performed by the instruction has been fulfilled.
@ -84,7 +84,7 @@ object AutoDriveControls {
override def Validate(vehicle : Vehicle) : Boolean = true
def CompletionTest(vehicle : Vehicle) = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero).xy) > 0
def CompletionTest(vehicle : Vehicle) = vehicle.isMoving
}
protected final case class AutoDriveDistance(start : Vector3, sqDistance : Float) extends Setting {
@ -162,7 +162,7 @@ object AutoDriveControls {
override def Data = Some(speed)
def CompletionTest(vehicle : Vehicle) = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero)) > 0
def CompletionTest(vehicle : Vehicle) = vehicle.isMoving
override def Validate(vehicle : Vehicle) : Boolean = {
speed = vehicle.Definition.AutoPilotSpeed1
@ -177,7 +177,7 @@ object AutoDriveControls {
override def Data = Some(speed)
def CompletionTest(vehicle : Vehicle) = Vector3.MagnitudeSquared(vehicle.Velocity.getOrElse(Vector3.Zero)) > 0
def CompletionTest(vehicle : Vehicle) = vehicle.isMoving
override def Validate(vehicle : Vehicle) : Boolean = {
speed = vehicle.Definition.AutoPilotSpeed2

View file

@ -21,7 +21,7 @@ object HackState extends Enumeration {
val
Unknown0,
Start,
Unknown2,
Cancelled,
Ongoing,
Finished,
Unknown5,

View file

@ -94,8 +94,8 @@ object PlayerStateMessage extends Marshallable[PlayerStateMessage] {
("pos" | Vector3.codec_pos) ::
optional(bool, "vel" | Vector3.codec_vel) ::
("facingYaw" | Angular.codec_yaw()) ::
("facingPitch" | Angular.codec_pitch) ::
("facingYawUpper" | Angular.codec_yaw(0f)) ::
("facingPitch" | Angular.codec_zero_centered) ::
("facingYawUpper" | Angular.codec_zero_centered) ::
("unk1" | uintL(10)) ::
(bool >>:~ { fourBools =>
newcodecs.binary_choice(!fourBools, booleanCodec, defaultCodec)

View file

@ -16,10 +16,13 @@ import scodec.codecs._
* @param pos where the player is in the world
* @param vel how the player is moving
* @param facingYaw a "yaw" angle
* @param facingPitch a "pitch" angle
* @param facingPitch a "pitch" angle;
* 0 for forward-facing;
* 75.9375 for the up-facing limit;
* -73.125 for the down-facing limit
* @param facingYawUpper a "yaw" angle that represents the angle of the avatar's upper body with respect to its forward-facing direction;
* this number is normally 0 for forward facing;
* the range is limited between approximately 61 degrees of center turned to left or right
* 0 for forward-facing;
* +/-61.875 for the clockwise/counterclockwise turn limits, respectively
* @param seq_time the "time frame" according to the server;
* starts at 0; max value is 1023 before resetting
* @param unk1 na
@ -59,8 +62,8 @@ object PlayerStateMessageUpstream extends Marshallable[PlayerStateMessageUpstrea
("pos" | Vector3.codec_pos) ::
("vel" | optional(bool, Vector3.codec_vel)) ::
("facingYaw" | Angular.codec_yaw()) ::
("facingPitch" | Angular.codec_pitch) ::
("facingYawUpper" | Angular.codec_yaw(0f)) ::
("facingPitch" | Angular.codec_zero_centered) ::
("facingYawUpper" | Angular.codec_zero_centered) ::
("seq_time" | uintL(10)) ::
("unk1" | uintL(3)) ::
("is_crouching" | bool) ::

View file

@ -357,8 +357,8 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
("unk2" | bool) :: //requires alt_model flag (does NOT require health == 0)
("unk3" | bool) :: //stream misalignment when set
("unk4" | bool) :: //unknown
("facingPitch" | Angular.codec_pitch) ::
("facingYawUpper" | Angular.codec_yaw(0f)) ::
("facingPitch" | Angular.codec_zero_centered) ::
("facingYawUpper" | Angular.codec_zero_centered) ::
("lfs" | uint2) ::
("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined)
("is_cloaking" | bool) ::

View file

@ -21,10 +21,7 @@ object Angular {
case _ :: roll :: HNil =>
roll
},
{
case roll : Float =>
() :: roll :: HNil
}
roll => () :: roll :: HNil
)
def codec_roll(bits : Int) : Codec[Float] = newcodecs.q_float(0.0f, 360.0f, bits)
@ -38,21 +35,12 @@ object Angular {
case _ :: pitch :: HNil =>
pitch
},
{
case pitch : Float =>
() :: pitch :: HNil
}
pitch => () :: pitch :: HNil
)
def codec_pitch(bits : Int) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] (
{
case pitch =>
decodeCorrectedAngle(pitch)
},
{
case pitch : Float =>
encodeCorrectedAngle(pitch)
}
pitch => decodeCorrectedAngle(pitch),
pitch => encodeCorrectedAngle(pitch)
)
//yaw
@ -64,20 +52,21 @@ object Angular {
case _ :: yaw :: HNil =>
yaw
},
{
case yaw : Float =>
() :: yaw :: HNil
}
yaw => () :: yaw :: HNil
)
def codec_yaw(bits : Int, North : Float) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] (
{
case yaw =>
decodeCorrectedAngle(yaw, North)
},
{
case yaw : Float =>
encodeCorrectedAngle(yaw, North)
yaw => decodeCorrectedAngle(yaw, North),
yaw => encodeCorrectedAngle(yaw, North)
)
val codec_zero_centered : Codec[Float] = codec_yaw(North = 0).xmap[Float] (
out => if(out > 180) out - 360 else out,
in => {
val adjustedIn = in % 360
if(adjustedIn < 0) 360 + adjustedIn
else if(adjustedIn > 180) 360 - adjustedIn
else adjustedIn
}
)

View file

@ -66,7 +66,7 @@ class PlayerStateMessageTest extends Specification {
vel.get.y mustEqual 6.5625f
vel.get.z mustEqual 0.0f
facingYaw mustEqual 22.5f
facingPitch mustEqual 348.75f
facingPitch mustEqual -11.25f
facingUpper mustEqual 0f
unk1 mustEqual 165
crouching mustEqual false
@ -105,7 +105,7 @@ class PlayerStateMessageTest extends Specification {
PlanetSideGUID(1696),
Vector3(4008.6016f, 5987.6016f, 44.1875f),
Some(Vector3(2.53125f, 6.5625f, 0f)),
22.5f, 348.75f, 0f, 165,
22.5f, -11.25f, 0f, 165,
false, false, false, false)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_vel

View file

@ -15,9 +15,9 @@ class PlayerStateMessageUpstreamTest extends Specification {
case PlayerStateMessageUpstream(avatar_guid, pos, vel, facingYaw, facingPitch, facingYawUpper, seq_time, unk1, is_crouching, is_jumping, jump_thrust, is_cloaked, unk2, unk3) =>
avatar_guid mustEqual PlanetSideGUID(75)
pos mustEqual Vector3(3694.1094f, 2735.4531f, 90.84375f)
vel mustEqual Some(Vector3(4.375f, 2.59375f, 0.0f))
vel.contains(Vector3(4.375f, 2.59375f, 0.0f)) mustEqual true
facingYaw mustEqual 61.875f
facingPitch mustEqual 351.5625f
facingPitch mustEqual -8.4375f
facingYawUpper mustEqual 0.0f
seq_time mustEqual 136
unk1 mustEqual 0
@ -33,7 +33,7 @@ class PlayerStateMessageUpstreamTest extends Specification {
}
"encode" in {
val msg = PlayerStateMessageUpstream(PlanetSideGUID(75), Vector3(3694.1094f, 2735.4531f, 90.84375f), Some(Vector3(4.375f, 2.59375f, 0.0f)), 61.875f, 351.5625f, 0.0f, 136, 0, false, false, false, false, 112, 0)
val msg = PlayerStateMessageUpstream(PlanetSideGUID(75), Vector3(3694.1094f, 2735.4531f, 90.84375f), Some(Vector3(4.375f, 2.59375f, 0.0f)), 61.875f, -8.4375f, 0.0f, 136, 0, false, false, false, false, 112, 0)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string

View file

@ -57,7 +57,7 @@ class CharacterDataTest extends Specification {
b.outfit_name mustEqual "Black Beret Armoured Corps"
b.outfit_logo mustEqual 23
b.backpack mustEqual false
b.facingPitch mustEqual 320.625f
b.facingPitch mustEqual -39.375f
b.facingYawUpper mustEqual 0
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
@ -176,7 +176,7 @@ class CharacterDataTest extends Specification {
b.outfit_name mustEqual "Black Beret Armoured Corps"
b.outfit_logo mustEqual 23
b.backpack mustEqual false
b.facingPitch mustEqual 320.625f
b.facingPitch mustEqual -39.375f
b.facingYawUpper mustEqual 0
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
@ -245,7 +245,7 @@ class CharacterDataTest extends Specification {
b.outfit_name mustEqual "Original District"
b.outfit_logo mustEqual 23
b.backpack mustEqual true
b.facingPitch mustEqual 351.5625f
b.facingPitch mustEqual -8.4375f
b.facingYawUpper mustEqual 0
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
@ -334,7 +334,7 @@ class CharacterDataTest extends Specification {
false,
false,
false,
320.625f, 0f,
-39.375f, 0f,
false,
GrenadeState.None,
false,
@ -414,7 +414,7 @@ class CharacterDataTest extends Specification {
false,
false,
false,
320.625f, 0f,
-39.375f, 0f,
false,
GrenadeState.None,
false,
@ -497,7 +497,7 @@ class CharacterDataTest extends Specification {
false, //unk2
false, //unk3
false, //unk4
351.5625f, 0f,
351.5625f, 0f, //also: -8.4375f, 0f
false, //lfs
GrenadeState.None,
false, //is_cloaking

View file

@ -458,7 +458,7 @@ class DetailedCharacterDataTest extends Specification {
b.outfit_name mustEqual ""
b.outfit_logo mustEqual 0
b.backpack mustEqual false
b.facingPitch mustEqual 348.75f
b.facingPitch mustEqual -11.25f
b.facingYawUpper mustEqual 0
b.lfs mustEqual true
b.grenade_state mustEqual GrenadeState.None
@ -666,8 +666,8 @@ class DetailedCharacterDataTest extends Specification {
b.outfit_name mustEqual ""
b.outfit_logo mustEqual 14
b.backpack mustEqual false
b.facingPitch mustEqual 348.75f
b.facingYawUpper mustEqual 348.75f
b.facingPitch mustEqual -11.25f
b.facingYawUpper mustEqual -11.25f
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
b.is_cloaking mustEqual false
@ -1759,7 +1759,7 @@ class DetailedCharacterDataTest extends Specification {
false,
false,
false,
348.75f, 0,
-11.25f, 0,
true,
GrenadeState.None,
false,
@ -1949,7 +1949,7 @@ class DetailedCharacterDataTest extends Specification {
false,
false,
false,
348.75f, 348.75f,
-11.25f, -11.25f,
false,
GrenadeState.None,
false,

View file

@ -71,7 +71,7 @@ class MountedVehiclesTest extends Specification {
b.outfit_name mustEqual "Black Beret Armoured Corps"
b.outfit_logo mustEqual 23
b.backpack mustEqual false
b.facingPitch mustEqual 348.75f
b.facingPitch mustEqual -11.25f
b.facingYawUpper mustEqual 0
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
@ -167,7 +167,7 @@ class MountedVehiclesTest extends Specification {
false,
false,
false,
348.75f, 0,
-11.25f, 0,
false,
GrenadeState.None,
false,

View file

@ -24,7 +24,7 @@ class EntityTest extends Specification {
val obj : EntityTestClass = new EntityTestClass()
obj.Position mustEqual Vector3(0f, 0f, 0f)
obj.Orientation mustEqual Vector3(0f, 0f, 0f)
obj.Velocity mustEqual None
obj.Velocity.isEmpty mustEqual true
}
"mutate and access" in {
@ -35,7 +35,7 @@ class EntityTest extends Specification {
obj.Position mustEqual Vector3(1f, 1f, 1f)
obj.Orientation mustEqual Vector3(2f, 2f, 2f)
obj.Velocity mustEqual Some(Vector3(3f, 3f, 3f))
obj.Velocity.contains(Vector3(3f, 3f, 3f)) mustEqual true
}
"clamp Orientation" in {
@ -43,6 +43,61 @@ class EntityTest extends Specification {
obj.Orientation = Vector3(-1f, 361f, -0f)
obj.Orientation mustEqual Vector3(359f, 1f, 0f)
}
"is moving (at all)" in {
val obj : EntityTestClass = new EntityTestClass
obj.Velocity.isEmpty mustEqual true
obj.isMoving mustEqual false
obj.Velocity = Vector3.Zero
obj.isMoving mustEqual false
obj.Velocity = Vector3(1,0,0)
obj.isMoving mustEqual true
obj.Velocity = None
obj.isMoving mustEqual false
}
"is moving (Vector3 comparison)" in {
val obj : EntityTestClass = new EntityTestClass
val test1 = Vector3(1,0,0)
val test2 = Vector3(2,0,0)
obj.Velocity.isEmpty mustEqual true
obj.isMoving mustEqual false
obj.isMoving(test1) mustEqual false
obj.isMoving(test2) mustEqual false
obj.Velocity = Vector3(1,0,0)
obj.isMoving(test1) mustEqual true
obj.isMoving(test2) mustEqual false
obj.Velocity = Vector3(3,0,0)
obj.isMoving(test1) mustEqual true
obj.isMoving(test2) mustEqual true
obj.Velocity = Vector3(1,1,0)
obj.isMoving(test1) mustEqual true
obj.isMoving(test2) mustEqual false
}
"is moving (Float comparison)" in {
val obj : EntityTestClass = new EntityTestClass
obj.Velocity.isEmpty mustEqual true
obj.isMoving mustEqual false
obj.isMoving(1) mustEqual false
obj.isMoving(2) mustEqual false
obj.isMoving(4) mustEqual false
obj.Velocity = Vector3(1,0,0)
obj.isMoving(1) mustEqual true
obj.isMoving(2) mustEqual false
obj.isMoving(4) mustEqual false
obj.Velocity = Vector3(3,0,0)
obj.isMoving(1) mustEqual true
obj.isMoving(2) mustEqual true
obj.isMoving(4) mustEqual true
obj.Velocity = Vector3(1,1,1)
obj.isMoving(1) mustEqual true
obj.isMoving(2) mustEqual true
obj.isMoving(4) mustEqual false
}
}
"IdentifiableEntity" should {

View file

@ -133,26 +133,26 @@ class PlayerTest extends Specification {
obj.Stamina mustEqual 456
}
"set new values (health, armor, stamina) but only when alive" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Health = 23
obj.Armor = 34
obj.Stamina = 45
obj.Health mustEqual 0
obj.Armor mustEqual 0
obj.Stamina mustEqual 0
obj.Spawn
obj.Health mustEqual obj.MaxHealth
obj.Armor mustEqual obj.MaxArmor
obj.Stamina mustEqual obj.MaxStamina
obj.Health = 23
obj.Armor = 34
obj.Stamina = 45
obj.Health mustEqual 23
obj.Armor mustEqual 34
obj.Stamina mustEqual 45
}
// "set new values (health, armor, stamina) but only when alive" in {
// val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
// obj.Health = 23
// obj.Armor = 34
// obj.Stamina = 45
// obj.Health mustEqual 0
// obj.Armor mustEqual 0
// obj.Stamina mustEqual 0
//
// obj.Spawn
// obj.Health mustEqual obj.MaxHealth
// obj.Armor mustEqual obj.MaxArmor
// obj.Stamina mustEqual obj.MaxStamina
// obj.Health = 23
// obj.Armor = 34
// obj.Stamina = 45
// obj.Health mustEqual 23
// obj.Armor mustEqual 34
// obj.Stamina mustEqual 45
// }
"has visible slots" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)

View file

@ -65,6 +65,7 @@ import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.util.Success
import akka.pattern.ask
import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity}
import net.psforever.objects.vehicles.Utility.InternalTelepad
import services.local.support.{HackCaptureActor, RouterTelepadActivation}
import services.support.SupportActor
@ -1480,7 +1481,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
(distanceSq < 10000 && time > 500) ||
(distanceSq < 160000 && (
(is_jumping || time < 200)) ||
((vel.isEmpty || Vector3.MagnitudeSquared(vel.get).toInt == 0) && time > 2000) ||
(!WorldEntity.isMoving(vel) && time > 2000) ||
(time > 1000)) ||
(distanceSq > 160000 && time > 5000)) {
sendResponse(
@ -3243,30 +3244,43 @@ class WorldSessionActor extends Actor with MDCContextAware {
def HandleHackingProgress(progressType : Int, tplayer : Player, target : PlanetSideServerObject, tool_guid : PlanetSideGUID, delta : Float, completeAction : ()=>Unit, tickAction : Option[()=>Unit]) : Unit = {
progressBarUpdate.cancel
if(progressBarValue.isDefined) {
val progressBarVal : Float = progressBarValue.get + delta
val progressBarVal : Float = if (progressBarValue.get + delta > 100) { 100f } else { progressBarValue.get + delta }
val vis = if(progressBarVal == 0L) {
//hack state for progress bar visibility
HackState.Start
}
else if(progressBarVal > 100L) {
else if(progressBarVal >= 100L) {
HackState.Finished
}
else if(target.Velocity.isDefined && Vector3.Distance(Vector3.Zero, target.Velocity.get) > 1f) {
// If the object is moving (more than slightly to account for things like magriders rotating, or the last velocity reported being the magrider dipping down on dismount) then cancel the hack
HackState.Cancelled
}
else {
HackState.Ongoing
}
sendResponse(HackMessage(progressType, target.GUID, player.GUID, progressBarVal.toInt, 0L, vis, 8L))
if(progressBarVal > 100) {
//done
progressBarValue = None
// sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L))
completeAction()
if(vis == HackState.Cancelled) {
// Object moved. Cancel the hack (e.g. vehicle drove away)
sendResponse(HackMessage(progressType, target.GUID, player.GUID, 0, 0L, vis, 8L))
}
else {
//continue next tick
tickAction.getOrElse(() => Unit)()
progressBarValue = Some(progressBarVal)
import scala.concurrent.ExecutionContext.Implicits.global
progressBarUpdate = context.system.scheduler.scheduleOnce(250 milliseconds, self, HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction))
else
{
sendResponse(HackMessage(progressType, target.GUID, player.GUID, progressBarVal.toInt, 0L, vis, 8L))
if(progressBarVal >= 100) {
//done
progressBarValue = None
completeAction()
}
else {
//continue next tick
tickAction.getOrElse(() => Unit)()
progressBarValue = Some(progressBarVal)
import scala.concurrent.ExecutionContext.Implicits.global
progressBarUpdate = context.system.scheduler.scheduleOnce(250 milliseconds, self, HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction))
}
}
}
}
@ -3510,7 +3524,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatar.Certifications += ReinforcedExoSuit
avatar.Certifications += ATV
avatar.Certifications += Harasser
//
avatar.Certifications += InfiltrationSuit
avatar.Certifications += Sniping
avatar.Certifications += AntiVehicular
@ -3537,6 +3550,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatar.Certifications += AssaultEngineering
avatar.Certifications += Hacking
avatar.Certifications += AdvancedHacking
avatar.Certifications += ElectronicsExpert
avatar.Certifications += Medical
avatar.Certifications += AdvancedMedical
avatar.CEP = 6000001
this.avatar = avatar
@ -3897,98 +3913,94 @@ class WorldSessionActor extends Actor with MDCContextAware {
self ! SetCurrentAvatar(player)
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, jump_thrust, is_cloaking, unk5, unk6) =>
if(deadState == DeadState.Alive) {
val time = System.currentTimeMillis()
if (timeDL != 0) {
if (time - timeDL > 500) {
player.Stamina = player.Stamina - 1
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
timeDL = time
}
}
if (timeSurge != 0) {
if (time - timeSurge > 500 && player.ExoSuit == ExoSuitType.Agile) {
player.Stamina = player.Stamina - 1
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
timeSurge = time
}
else if (time - timeSurge > 333 && player.ExoSuit == ExoSuitType.Reinforced) {
player.Stamina = player.Stamina - 1
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
timeSurge = time
}
else if (time - 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 = time
}
}
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
//if(deadState == DeadState.Alive) {
val time = System.currentTimeMillis()
if (timeDL != 0) {
if (time - timeDL > 500) {
player.Stamina = player.Stamina - 1
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
timeDL = time
}
player.Position = pos
player.Velocity = vel
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
player.FacingYawUpper = yaw_upper
player.Crouching = is_crouching
player.Jumping = is_jumping
player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && is_cloaking
}
if (timeSurge != 0) {
if (time - timeSurge > 500 && player.ExoSuit == ExoSuitType.Agile) {
player.Stamina = player.Stamina - 1
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
timeSurge = time
}
else if (time - timeSurge > 333 && player.ExoSuit == ExoSuitType.Reinforced) {
player.Stamina = player.Stamina - 1
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
timeSurge = time
}
else if (time - 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 = time
}
}
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
}
}
val isMoving = WorldEntity.isMoving(vel)
if (!isMoving && 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)
player.FacingYawUpper = yaw_upper
player.Crouching = is_crouching
player.Jumping = is_jumping
player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && is_cloaking
if(vel.isDefined && usingMedicalTerminal.isDefined) {
continent.GUID(usingMedicalTerminal) match {
case Some(term : Terminal with ProximityUnit) =>
StopUsingProximityUnit(term)
case _ => ;
if(isMoving && usingMedicalTerminal.isDefined) {
continent.GUID(usingMedicalTerminal) match {
case Some(term : Terminal with ProximityUnit) =>
StopUsingProximityUnit(term)
case _ => ;
}
}
accessedContainer match {
case Some(veh : Vehicle) =>
if(isMoving || Vector3.DistanceSquared(player.Position, veh.Position) > 100) {
val guid = player.GUID
sendResponse(UnuseItemMessage(guid, veh.GUID))
sendResponse(UnuseItemMessage(guid, guid))
veh.AccessingTrunk = None
UnAccessContents(veh)
accessedContainer = None
}
}
accessedContainer match {
case Some(veh : Vehicle) =>
if(vel.isDefined || Vector3.DistanceSquared(player.Position, veh.Position) > 100) {
val guid = player.GUID
sendResponse(UnuseItemMessage(guid, veh.GUID))
sendResponse(UnuseItemMessage(guid, guid))
veh.AccessingTrunk = None
UnAccessContents(veh)
accessedContainer = None
case Some(container) => //just in case
if(isMoving) {
val guid = player.GUID
// If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first.
if(container.HasGUID) {
sendResponse(UnuseItemMessage(guid, container.GUID))
}
case Some(container) => //just in case
if(vel.isDefined) {
val guid = player.GUID
// If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first.
if(container.HasGUID) {
sendResponse(UnuseItemMessage(guid, container.GUID))
}
sendResponse(UnuseItemMessage(guid, guid))
accessedContainer = None
}
case None => ;
}
val wepInHand : Boolean = player.Slot(player.DrawnSlot).Equipment match {
case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
case None => false
}
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, player.Position, player.Velocity, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectator, wepInHand))
updateSquad()
sendResponse(UnuseItemMessage(guid, guid))
accessedContainer = None
}
case None => ;
}
else {
timeDL = 0
timeSurge = 0
val wepInHand : Boolean = player.Slot(player.DrawnSlot).Equipment match {
case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
case None => false
}
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, player.Position, player.Velocity, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectator, wepInHand))
updateSquad()
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
@ -4839,7 +4851,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
accessedContainer = Some(obj)
}
else if(!unk3) { //potential kit use
else if(!unk3 && player.isAlive) { //potential kit use
continent.GUID(item_used_guid) match {
case Some(kit : Kit) =>
player.Find(kit) match {
@ -4853,7 +4865,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
else {
player.Find(kit) match {
case Some(index) =>
case Some(index) =>
whenUsedLastKit = 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))
@ -4957,7 +4969,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if (tool.Definition == GlobalDefinitions.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 && tplayer.MaxArmor > 0) {
if (player.GUID != tplayer.GUID && Vector3.Distance(player.Position, tplayer.Position) < 5 && player.Faction == tplayer.Faction && !player.isMoving && tplayer.MaxArmor > 0 && tplayer.Armor < tplayer.MaxArmor) {
tplayer.Armor += 15
tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine))
@ -4965,7 +4977,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(RepairMessage(object_guid, RepairPercent))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 4, tplayer.Armor))
} else if (player.GUID == tplayer.GUID && player.Velocity.isEmpty && tplayer.MaxArmor > 0) {
} else if (player.GUID == tplayer.GUID && !player.isMoving && tplayer.MaxArmor > 0) {
player.Armor += 15
tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine))
@ -4976,8 +4988,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
} else if (tool.Definition == GlobalDefinitions.medicalapplicator) {
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 && tplayer.MaxHealth > 0) {
tplayer.Health += 10
if (player.GUID != tplayer.GUID && Vector3.Distance(player.Position, tplayer.Position) < 5 && player.Faction == tplayer.Faction && !player.isMoving && tplayer.MaxHealth > 0 && tplayer.Health < tplayer.MaxHealth) {
if(tplayer.isAlive) {
tplayer.Health += 10
} else {
// Reviving another player is normally 25 "medical energy" (ammo) and 5,000 milliseconds duration, based on the game properties revive_ammo_required and revive_time
//todo: @NotEnoughAmmoToRevive=You do not have enough medical energy to revive this corpse.
tplayer.Health += 4 // 4 health per tick = 5 second revive timer from 0 health
}
tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine))
val repairPercent: Int = tplayer.Health * 100 / tplayer.MaxHealth
@ -4991,7 +5009,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(tplayer.isAlive) {
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 0, tplayer.Health))
}
} else if (player.GUID == tplayer.GUID && player.Velocity.isEmpty && tplayer.MaxHealth > 0) {
} else if (player.GUID == tplayer.GUID && !player.isMoving && tplayer.MaxHealth > 0 && player.isAlive) {
player.Health += 10
tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine))
@ -5124,7 +5142,7 @@ 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 (!player.isMoving && Vector3.Distance(player.Position, obj.Position) < 5) {
if (obj.Health < obj.MaxHealth) {
obj.Health += 48
// sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
@ -5499,7 +5517,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
val (angle, attribution, acceptableDistanceToOwner) = obj match {
case p : Player =>
(p.Orientation, tool.Definition.ObjectId, 10f + (if(p.Velocity.nonEmpty) { 5f } else { 0f })) //TODO upper body facing
(SimpleWorldEntity.validateOrientationEntry(p.Orientation + Vector3.z(p.FacingYawUpper)), tool.Definition.ObjectId, 10f + (if(p.Velocity.nonEmpty) { 5f } else { 0f }))
case v : Vehicle if v.Definition.CanFly =>
(tool.Orientation, obj.Definition.ObjectId, 1000f) //TODO this is too simplistic to find proper angle
case _ : Vehicle =>
@ -7817,6 +7835,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val respawnTimer = 300000 //milliseconds
tplayer.Die
deadState = DeadState.Dead
timeDL = 0
timeSurge = 0
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))

View file

@ -369,8 +369,8 @@ class PacketCodingActorETest extends ActorTest {
"PacketCodingActor" should {
"unwind l-originating hexadecimal data into multiple r-facing packets (MultiPacket -> 2 PlayerStateMessageUpstream)" in {
val string_hex = RawPacket(hex"00 03 18 BD E8 04 5C 02 60 E3 F9 19 0E C1 41 27 00 04 02 60 20 0C 58 0B 20 00 00 18 BD E8 04 86 02 62 13 F9 19 0E D8 40 4D 00 04 02 60 20 0C 78 0A 80 00 00")
val string_obj1 = GamePacket(GamePacketOpcode.PlayerStateMessageUpstream, 0, PlayerStateMessageUpstream(PlanetSideGUID(1256),Vector3(3076.7188f,4734.1094f,56.390625f),Some(Vector3(4.0625f,4.59375f,0.0f)),36.5625f,357.1875f,0.0f,866,0,false,false,false,false,178,0))
val string_obj2 = GamePacket(GamePacketOpcode.PlayerStateMessageUpstream, 0, PlayerStateMessageUpstream(PlanetSideGUID(1256),Vector3(3077.0469f,4734.258f,56.390625f),Some(Vector3(5.5f,1.1875f,0.0f)),36.5625f,357.1875f,0.0f,867,0,false,false,false,false,168,0))
val string_obj1 = GamePacket(GamePacketOpcode.PlayerStateMessageUpstream, 0, PlayerStateMessageUpstream(PlanetSideGUID(1256),Vector3(3076.7188f,4734.1094f,56.390625f),Some(Vector3(4.0625f,4.59375f,0.0f)),36.5625f,-2.8125f,0.0f,866,0,false,false,false,false,178,0))
val string_obj2 = GamePacket(GamePacketOpcode.PlayerStateMessageUpstream, 0, PlayerStateMessageUpstream(PlanetSideGUID(1256),Vector3(3077.0469f,4734.258f,56.390625f),Some(Vector3(5.5f,1.1875f,0.0f)),36.5625f,-2.8125f,0.0f,867,0,false,false,false,false,168,0))
val probe1 = TestProbe()
val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe")
@ -454,7 +454,7 @@ class PacketCodingActorHTest extends ActorTest {
class PacketCodingActorITest extends ActorTest {
import net.psforever.packet.game.objectcreate._
val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
val app : Int=>CharacterAppearanceData = CharacterAppearanceData(
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1),
false,
false,
@ -462,7 +462,7 @@ class PacketCodingActorITest extends ActorTest {
"",
0,
false,
2.8125f, 210.9375f,
2.8125f, 0f,
true,
GrenadeState.None,
false,
@ -470,7 +470,7 @@ class PacketCodingActorITest extends ActorTest {
None,
RibbonBars()
)
var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
var char : Option[Int]=>DetailedCharacterData = DetailedCharacterData(
0,
0,
100, 100,
@ -484,8 +484,9 @@ class PacketCodingActorITest extends ActorTest {
None
)
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
//println(s"${PacketCoding.EncodePacket(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj))}")
val pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
val string_hex = hex"00090000186c060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c00490084524000000000000000000000000000000020000007f35703fffffffffffffffffffffffffffffffc000000000000000000000000000000000000000190019000640000000000c800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000100000000400e0"
val string_hex = hex"00090000186c060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c00490084524000000000000000000000000000000020000007f00703fffffffffffffffffffffffffffffffc000000000000000000000000000000000000000190019000640000000000c800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000100000000400e0"
"PacketCodingActor" should {
"bundle an r-originating packet into an l-facing SlottedMetaPacket byte stream data (SlottedMetaPacket)" in {
@ -582,7 +583,7 @@ class PacketCodingActorKTest extends ActorTest {
false,
false,
false,
2.8125f, 210.9375f,
2.8125f, 0f,
false,
GrenadeState.None,
false,
@ -593,7 +594,7 @@ class PacketCodingActorKTest extends ActorTest {
None
)
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
val app : Int=>CharacterAppearanceData = CharacterAppearanceData(
aa, ab,
RibbonBars()
)
@ -635,7 +636,7 @@ class PacketCodingActorKTest extends ActorTest {
Nil, Nil, false,
None
)
val char : (Option[Int])=>DetailedCharacterData =
val char : Option[Int]=>DetailedCharacterData =
(pad_length : Option[Int]) => DetailedCharacterData(ba, bb(ba.bep, pad_length))(pad_length)
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
val list = List(