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 : Option[Vector3]) : Option[Vector3]
def Velocity_=(vec : Vector3) : Option[Vector3] = Velocity = Some(vec) 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 { 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 = { def toString(obj : WorldEntity) : String = {
s"pos=${obj.Position}, ori=${obj.Orientation}" 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.affinity.FactionAffinity
import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.turret.TurretDefinition import net.psforever.objects.serverobject.turret.TurretDefinition
import net.psforever.types.Vector3
object MountableBehavior { object MountableBehavior {
/** /**
@ -79,10 +78,9 @@ object MountableBehavior {
val dismountBehavior : Receive = { val dismountBehavior : Receive = {
case Mountable.TryDismount(user, seat_num) => case Mountable.TryDismount(user, seat_num) =>
val obj = MountableObject val obj = MountableObject
val velocity = obj.Velocity.getOrElse(Vector3.Zero)
obj.Seat(seat_num) match { obj.Seat(seat_num) match {
case Some(seat) => case Some(seat) =>
if(seat.Bailable || velocity == Vector3.Zero || Vector3.MagnitudeSquared(velocity).toInt == 0) { if(seat.Bailable || !obj.isMoving) {
seat.Occupant = None seat.Occupant = None
user.VehicleSeated = None user.VehicleSeated = None
sender ! Mountable.MountMessages(user, Mountable.CanDismount(obj, seat_num)) sender ! Mountable.MountMessages(user, Mountable.CanDismount(obj, seat_num))

View file

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

View file

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

View file

@ -94,8 +94,8 @@ object PlayerStateMessage extends Marshallable[PlayerStateMessage] {
("pos" | Vector3.codec_pos) :: ("pos" | Vector3.codec_pos) ::
optional(bool, "vel" | Vector3.codec_vel) :: optional(bool, "vel" | Vector3.codec_vel) ::
("facingYaw" | Angular.codec_yaw()) :: ("facingYaw" | Angular.codec_yaw()) ::
("facingPitch" | Angular.codec_pitch) :: ("facingPitch" | Angular.codec_zero_centered) ::
("facingYawUpper" | Angular.codec_yaw(0f)) :: ("facingYawUpper" | Angular.codec_zero_centered) ::
("unk1" | uintL(10)) :: ("unk1" | uintL(10)) ::
(bool >>:~ { fourBools => (bool >>:~ { fourBools =>
newcodecs.binary_choice(!fourBools, booleanCodec, defaultCodec) 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 pos where the player is in the world
* @param vel how the player is moving * @param vel how the player is moving
* @param facingYaw a "yaw" angle * @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; * @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; * 0 for forward-facing;
* the range is limited between approximately 61 degrees of center turned to left or right * +/-61.875 for the clockwise/counterclockwise turn limits, respectively
* @param seq_time the "time frame" according to the server; * @param seq_time the "time frame" according to the server;
* starts at 0; max value is 1023 before resetting * starts at 0; max value is 1023 before resetting
* @param unk1 na * @param unk1 na
@ -59,8 +62,8 @@ object PlayerStateMessageUpstream extends Marshallable[PlayerStateMessageUpstrea
("pos" | Vector3.codec_pos) :: ("pos" | Vector3.codec_pos) ::
("vel" | optional(bool, Vector3.codec_vel)) :: ("vel" | optional(bool, Vector3.codec_vel)) ::
("facingYaw" | Angular.codec_yaw()) :: ("facingYaw" | Angular.codec_yaw()) ::
("facingPitch" | Angular.codec_pitch) :: ("facingPitch" | Angular.codec_zero_centered) ::
("facingYawUpper" | Angular.codec_yaw(0f)) :: ("facingYawUpper" | Angular.codec_zero_centered) ::
("seq_time" | uintL(10)) :: ("seq_time" | uintL(10)) ::
("unk1" | uintL(3)) :: ("unk1" | uintL(3)) ::
("is_crouching" | bool) :: ("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) ("unk2" | bool) :: //requires alt_model flag (does NOT require health == 0)
("unk3" | bool) :: //stream misalignment when set ("unk3" | bool) :: //stream misalignment when set
("unk4" | bool) :: //unknown ("unk4" | bool) :: //unknown
("facingPitch" | Angular.codec_pitch) :: ("facingPitch" | Angular.codec_zero_centered) ::
("facingYawUpper" | Angular.codec_yaw(0f)) :: ("facingYawUpper" | Angular.codec_zero_centered) ::
("lfs" | uint2) :: ("lfs" | uint2) ::
("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined) ("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined)
("is_cloaking" | bool) :: ("is_cloaking" | bool) ::

View file

@ -21,10 +21,7 @@ object Angular {
case _ :: roll :: HNil => case _ :: roll :: HNil =>
roll roll
}, },
{ roll => () :: roll :: HNil
case roll : Float =>
() :: roll :: HNil
}
) )
def codec_roll(bits : Int) : Codec[Float] = newcodecs.q_float(0.0f, 360.0f, bits) def codec_roll(bits : Int) : Codec[Float] = newcodecs.q_float(0.0f, 360.0f, bits)
@ -38,21 +35,12 @@ object Angular {
case _ :: pitch :: HNil => case _ :: pitch :: HNil =>
pitch pitch
}, },
{ pitch => () :: pitch :: HNil
case pitch : Float =>
() :: pitch :: HNil
}
) )
def codec_pitch(bits : Int) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] ( def codec_pitch(bits : Int) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] (
{ pitch => decodeCorrectedAngle(pitch),
case pitch => pitch => encodeCorrectedAngle(pitch)
decodeCorrectedAngle(pitch)
},
{
case pitch : Float =>
encodeCorrectedAngle(pitch)
}
) )
//yaw //yaw
@ -64,20 +52,21 @@ object Angular {
case _ :: yaw :: HNil => case _ :: yaw :: HNil =>
yaw yaw
}, },
{ yaw => () :: yaw :: HNil
case yaw : Float =>
() :: yaw :: HNil
}
) )
def codec_yaw(bits : Int, North : Float) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] ( def codec_yaw(bits : Int, North : Float) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] (
{ yaw => decodeCorrectedAngle(yaw, North),
case yaw => yaw => encodeCorrectedAngle(yaw, North)
decodeCorrectedAngle(yaw, North) )
},
{ val codec_zero_centered : Codec[Float] = codec_yaw(North = 0).xmap[Float] (
case yaw : Float => out => if(out > 180) out - 360 else out,
encodeCorrectedAngle(yaw, North) 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.y mustEqual 6.5625f
vel.get.z mustEqual 0.0f vel.get.z mustEqual 0.0f
facingYaw mustEqual 22.5f facingYaw mustEqual 22.5f
facingPitch mustEqual 348.75f facingPitch mustEqual -11.25f
facingUpper mustEqual 0f facingUpper mustEqual 0f
unk1 mustEqual 165 unk1 mustEqual 165
crouching mustEqual false crouching mustEqual false
@ -105,7 +105,7 @@ class PlayerStateMessageTest extends Specification {
PlanetSideGUID(1696), PlanetSideGUID(1696),
Vector3(4008.6016f, 5987.6016f, 44.1875f), Vector3(4008.6016f, 5987.6016f, 44.1875f),
Some(Vector3(2.53125f, 6.5625f, 0f)), Some(Vector3(2.53125f, 6.5625f, 0f)),
22.5f, 348.75f, 0f, 165, 22.5f, -11.25f, 0f, 165,
false, false, false, false) false, false, false, false)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_vel 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) => 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) avatar_guid mustEqual PlanetSideGUID(75)
pos mustEqual Vector3(3694.1094f, 2735.4531f, 90.84375f) 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 facingYaw mustEqual 61.875f
facingPitch mustEqual 351.5625f facingPitch mustEqual -8.4375f
facingYawUpper mustEqual 0.0f facingYawUpper mustEqual 0.0f
seq_time mustEqual 136 seq_time mustEqual 136
unk1 mustEqual 0 unk1 mustEqual 0
@ -33,7 +33,7 @@ class PlayerStateMessageUpstreamTest extends Specification {
} }
"encode" in { "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 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

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

View file

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

View file

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

View file

@ -24,7 +24,7 @@ class EntityTest extends Specification {
val obj : EntityTestClass = new EntityTestClass() val obj : EntityTestClass = new EntityTestClass()
obj.Position mustEqual Vector3(0f, 0f, 0f) obj.Position mustEqual Vector3(0f, 0f, 0f)
obj.Orientation 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 { "mutate and access" in {
@ -35,7 +35,7 @@ class EntityTest extends Specification {
obj.Position mustEqual Vector3(1f, 1f, 1f) obj.Position mustEqual Vector3(1f, 1f, 1f)
obj.Orientation mustEqual Vector3(2f, 2f, 2f) 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 { "clamp Orientation" in {
@ -43,6 +43,61 @@ class EntityTest extends Specification {
obj.Orientation = Vector3(-1f, 361f, -0f) obj.Orientation = Vector3(-1f, 361f, -0f)
obj.Orientation mustEqual Vector3(359f, 1f, 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 { "IdentifiableEntity" should {

View file

@ -133,26 +133,26 @@ class PlayerTest extends Specification {
obj.Stamina mustEqual 456 obj.Stamina mustEqual 456
} }
"set new values (health, armor, stamina) but only when alive" in { // "set new values (health, armor, stamina) but only when alive" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) // val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
obj.Health = 23 // obj.Health = 23
obj.Armor = 34 // obj.Armor = 34
obj.Stamina = 45 // obj.Stamina = 45
obj.Health mustEqual 0 // obj.Health mustEqual 0
obj.Armor mustEqual 0 // obj.Armor mustEqual 0
obj.Stamina mustEqual 0 // obj.Stamina mustEqual 0
//
obj.Spawn // obj.Spawn
obj.Health mustEqual obj.MaxHealth // obj.Health mustEqual obj.MaxHealth
obj.Armor mustEqual obj.MaxArmor // obj.Armor mustEqual obj.MaxArmor
obj.Stamina mustEqual obj.MaxStamina // obj.Stamina mustEqual obj.MaxStamina
obj.Health = 23 // obj.Health = 23
obj.Armor = 34 // obj.Armor = 34
obj.Stamina = 45 // obj.Stamina = 45
obj.Health mustEqual 23 // obj.Health mustEqual 23
obj.Armor mustEqual 34 // obj.Armor mustEqual 34
obj.Stamina mustEqual 45 // obj.Stamina mustEqual 45
} // }
"has visible slots" in { "has visible slots" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5) 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.concurrent.duration._
import scala.util.Success import scala.util.Success
import akka.pattern.ask import akka.pattern.ask
import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity}
import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.vehicles.Utility.InternalTelepad
import services.local.support.{HackCaptureActor, RouterTelepadActivation} import services.local.support.{HackCaptureActor, RouterTelepadActivation}
import services.support.SupportActor import services.support.SupportActor
@ -1480,7 +1481,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
(distanceSq < 10000 && time > 500) || (distanceSq < 10000 && time > 500) ||
(distanceSq < 160000 && ( (distanceSq < 160000 && (
(is_jumping || time < 200)) || (is_jumping || time < 200)) ||
((vel.isEmpty || Vector3.MagnitudeSquared(vel.get).toInt == 0) && time > 2000) || (!WorldEntity.isMoving(vel) && time > 2000) ||
(time > 1000)) || (time > 1000)) ||
(distanceSq > 160000 && time > 5000)) { (distanceSq > 160000 && time > 5000)) {
sendResponse( 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 = { def HandleHackingProgress(progressType : Int, tplayer : Player, target : PlanetSideServerObject, tool_guid : PlanetSideGUID, delta : Float, completeAction : ()=>Unit, tickAction : Option[()=>Unit]) : Unit = {
progressBarUpdate.cancel progressBarUpdate.cancel
if(progressBarValue.isDefined) { 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) { val vis = if(progressBarVal == 0L) {
//hack state for progress bar visibility //hack state for progress bar visibility
HackState.Start HackState.Start
} }
else if(progressBarVal > 100L) { else if(progressBarVal >= 100L) {
HackState.Finished 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 { else {
HackState.Ongoing HackState.Ongoing
} }
sendResponse(HackMessage(progressType, target.GUID, player.GUID, progressBarVal.toInt, 0L, vis, 8L))
if(progressBarVal > 100) { if(vis == HackState.Cancelled) {
//done // Object moved. Cancel the hack (e.g. vehicle drove away)
progressBarValue = None sendResponse(HackMessage(progressType, target.GUID, player.GUID, 0, 0L, vis, 8L))
// sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L))
completeAction()
} }
else { else
//continue next tick {
tickAction.getOrElse(() => Unit)() sendResponse(HackMessage(progressType, target.GUID, player.GUID, progressBarVal.toInt, 0L, vis, 8L))
progressBarValue = Some(progressBarVal)
import scala.concurrent.ExecutionContext.Implicits.global if(progressBarVal >= 100) {
progressBarUpdate = context.system.scheduler.scheduleOnce(250 milliseconds, self, HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction)) //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 += ReinforcedExoSuit
avatar.Certifications += ATV avatar.Certifications += ATV
avatar.Certifications += Harasser avatar.Certifications += Harasser
//
avatar.Certifications += InfiltrationSuit avatar.Certifications += InfiltrationSuit
avatar.Certifications += Sniping avatar.Certifications += Sniping
avatar.Certifications += AntiVehicular avatar.Certifications += AntiVehicular
@ -3537,6 +3550,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatar.Certifications += AssaultEngineering avatar.Certifications += AssaultEngineering
avatar.Certifications += Hacking avatar.Certifications += Hacking
avatar.Certifications += AdvancedHacking avatar.Certifications += AdvancedHacking
avatar.Certifications += ElectronicsExpert
avatar.Certifications += Medical
avatar.Certifications += AdvancedMedical
avatar.CEP = 6000001 avatar.CEP = 6000001
this.avatar = avatar this.avatar = avatar
@ -3897,98 +3913,94 @@ class WorldSessionActor extends Actor with MDCContextAware {
self ! SetCurrentAvatar(player) 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) => 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) { //if(deadState == DeadState.Alive) {
val time = System.currentTimeMillis() val time = System.currentTimeMillis()
if (timeDL != 0) { if (timeDL != 0) {
if (time - timeDL > 500) { if (time - timeDL > 500) {
player.Stamina = player.Stamina - 1 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
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina)) avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
timeDL = time
} }
player.Position = pos }
player.Velocity = vel if (timeSurge != 0) {
player.Orientation = Vector3(player.Orientation.x, pitch, yaw) if (time - timeSurge > 500 && player.ExoSuit == ExoSuitType.Agile) {
player.FacingYawUpper = yaw_upper player.Stamina = player.Stamina - 1
player.Crouching = is_crouching avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttributeSelf(player.GUID, 2, player.Stamina))
player.Jumping = is_jumping timeSurge = time
player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && is_cloaking }
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) { if(isMoving && usingMedicalTerminal.isDefined) {
continent.GUID(usingMedicalTerminal) match { continent.GUID(usingMedicalTerminal) match {
case Some(term : Terminal with ProximityUnit) => case Some(term : Terminal with ProximityUnit) =>
StopUsingProximityUnit(term) StopUsingProximityUnit(term)
case _ => ; 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
} }
} case Some(container) => //just in case
accessedContainer match { if(isMoving) {
case Some(veh : Vehicle) => val guid = player.GUID
if(vel.isDefined || Vector3.DistanceSquared(player.Position, veh.Position) > 100) { // 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.
val guid = player.GUID if(container.HasGUID) {
sendResponse(UnuseItemMessage(guid, veh.GUID)) sendResponse(UnuseItemMessage(guid, container.GUID))
sendResponse(UnuseItemMessage(guid, guid))
veh.AccessingTrunk = None
UnAccessContents(veh)
accessedContainer = None
} }
case Some(container) => //just in case sendResponse(UnuseItemMessage(guid, guid))
if(vel.isDefined) { accessedContainer = None
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. case None => ;
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()
} }
else { val wepInHand : Boolean = player.Slot(player.DrawnSlot).Equipment match {
timeDL = 0 case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
timeSurge = 0 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) => 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 //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)) sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
accessedContainer = Some(obj) accessedContainer = Some(obj)
} }
else if(!unk3) { //potential kit use else if(!unk3 && player.isAlive) { //potential kit use
continent.GUID(item_used_guid) match { continent.GUID(item_used_guid) match {
case Some(kit : Kit) => case Some(kit : Kit) =>
player.Find(kit) match { player.Find(kit) match {
@ -4853,7 +4865,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
else { else {
player.Find(kit) match { player.Find(kit) match {
case Some(index) => case Some(index) =>
whenUsedLastKit = System.currentTimeMillis whenUsedLastKit = System.currentTimeMillis
player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet 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(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) { if (tool.Definition == GlobalDefinitions.bank) {
continent.GUID(object_guid) match { continent.GUID(object_guid) match {
case Some(tplayer: Player) => 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 tplayer.Armor += 15
tool.Discharge tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine)) 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)) sendResponse(RepairMessage(object_guid, RepairPercent))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 4, tplayer.Armor)) 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 player.Armor += 15
tool.Discharge tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine)) 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) { } else if (tool.Definition == GlobalDefinitions.medicalapplicator) {
continent.GUID(object_guid) match { continent.GUID(object_guid) match {
case Some(tplayer: Player) => 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) { 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) {
tplayer.Health += 10 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 tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine)) sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine))
val repairPercent: Int = tplayer.Health * 100 / tplayer.MaxHealth val repairPercent: Int = tplayer.Health * 100 / tplayer.MaxHealth
@ -4991,7 +5009,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(tplayer.isAlive) { if(tplayer.isAlive) {
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 0, tplayer.Health)) 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 player.Health += 10
tool.Discharge tool.Discharge
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine)) sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, obj.GUID, tool.Magazine))
@ -5124,7 +5142,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
equipment.get.Definition match { equipment.get.Definition match {
case GlobalDefinitions.nano_dispenser => case GlobalDefinitions.nano_dispenser =>
//TODO repairing behavior //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) { if (obj.Health < obj.MaxHealth) {
obj.Health += 48 obj.Health += 48
// sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left)) // sendResponse(QuantityUpdateMessage(PlanetSideGUID(8214),ammo_quantity_left))
@ -5499,7 +5517,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
val (angle, attribution, acceptableDistanceToOwner) = obj match { val (angle, attribution, acceptableDistanceToOwner) = obj match {
case p : Player => 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 => case v : Vehicle if v.Definition.CanFly =>
(tool.Orientation, obj.Definition.ObjectId, 1000f) //TODO this is too simplistic to find proper angle (tool.Orientation, obj.Definition.ObjectId, 1000f) //TODO this is too simplistic to find proper angle
case _ : Vehicle => case _ : Vehicle =>
@ -7817,6 +7835,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val respawnTimer = 300000 //milliseconds val respawnTimer = 300000 //milliseconds
tplayer.Die tplayer.Die
deadState = DeadState.Dead deadState = DeadState.Dead
timeDL = 0
timeSurge = 0
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0)) sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0)) sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))

View file

@ -369,8 +369,8 @@ class PacketCodingActorETest extends ActorTest {
"PacketCodingActor" should { "PacketCodingActor" should {
"unwind l-originating hexadecimal data into multiple r-facing packets (MultiPacket -> 2 PlayerStateMessageUpstream)" in { "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_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_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,357.1875f,0.0f,867,0,false,false,false,false,168,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 probe1 = TestProbe()
val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe") val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe")
@ -454,7 +454,7 @@ class PacketCodingActorHTest extends ActorTest {
class PacketCodingActorITest extends ActorTest { class PacketCodingActorITest extends ActorTest {
import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.objectcreate._
val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero) 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), BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1),
false, false,
false, false,
@ -462,7 +462,7 @@ class PacketCodingActorITest extends ActorTest {
"", "",
0, 0,
false, false,
2.8125f, 210.9375f, 2.8125f, 0f,
true, true,
GrenadeState.None, GrenadeState.None,
false, false,
@ -470,7 +470,7 @@ class PacketCodingActorITest extends ActorTest {
None, None,
RibbonBars() RibbonBars()
) )
var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData( var char : Option[Int]=>DetailedCharacterData = DetailedCharacterData(
0, 0,
0, 0,
100, 100, 100, 100,
@ -484,8 +484,9 @@ class PacketCodingActorITest extends ActorTest {
None None
) )
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.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 pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
val string_hex = hex"00090000186c060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c00490084524000000000000000000000000000000020000007f35703fffffffffffffffffffffffffffffffc000000000000000000000000000000000000000190019000640000000000c800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000100000000400e0" val string_hex = hex"00090000186c060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c00490084524000000000000000000000000000000020000007f00703fffffffffffffffffffffffffffffffc000000000000000000000000000000000000000190019000640000000000c800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000100000000400e0"
"PacketCodingActor" should { "PacketCodingActor" should {
"bundle an r-originating packet into an l-facing SlottedMetaPacket byte stream data (SlottedMetaPacket)" in { "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, false,
false, false,
2.8125f, 210.9375f, 2.8125f, 0f,
false, false,
GrenadeState.None, GrenadeState.None,
false, false,
@ -593,7 +594,7 @@ class PacketCodingActorKTest extends ActorTest {
None None
) )
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData( val app : Int=>CharacterAppearanceData = CharacterAppearanceData(
aa, ab, aa, ab,
RibbonBars() RibbonBars()
) )
@ -635,7 +636,7 @@ class PacketCodingActorKTest extends ActorTest {
Nil, Nil, false, Nil, Nil, false,
None None
) )
val char : (Option[Int])=>DetailedCharacterData = val char : Option[Int]=>DetailedCharacterData =
(pad_length : Option[Int]) => DetailedCharacterData(ba, bb(ba.bep, pad_length))(pad_length) (pad_length : Option[Int]) => DetailedCharacterData(ba, bb(ba.bep, pad_length))(pad_length)
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None) val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
val list = List( val list = List(