diff --git a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala index bba719639..209809a6e 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -69,27 +69,24 @@ class SessionAvatarHandlers( isJumping, jumpThrust, isCloaking, - spectating, + isNotRendered, canSeeReallyFar ) if isNotSameTarget => val pstateToSave = pstate.copy(timestamp = 0) - val (lastMsg, lastTime, lastPosition, wasSpectating, wasVisible) = lastSeenStreamMessage(guid.guid) match { - case SessionAvatarHandlers.LastUpstream(Some(msg), visible, time) => (Some(msg), time, msg.pos, msg.spectator, visible) - case _ => (None, 0L, Vector3.Zero, false, false) + val (lastMsg, lastTime, lastPosition, wasVisible) = lastSeenStreamMessage(guid.guid) match { + case SessionAvatarHandlers.LastUpstream(Some(msg), visible, time) => (Some(msg), time, msg.pos, visible) + case _ => (None, 0L, Vector3.Zero, false) } - val drawConfig = Config.app.game.playerDraw + val drawConfig = Config.app.game.playerDraw //m val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m - val ourPosition = player.Position + val ourPosition = player.Position //xyz val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m - val inVisibleRange = currentDistance <= maxRange - val wasInVisibleRange = Vector3.DistanceSquared(lastPosition, pos) <= maxRange - val comingIntoVisibleRange = inVisibleRange && !wasInVisibleRange + val inDrawableRange = currentDistance <= maxRange val now = System.currentTimeMillis() //ms - val durationSince = now - lastTime //ms - val released = player.isReleased - if (!released && - !spectating && - (comingIntoVisibleRange || (inVisibleRange && !lastMsg.contains(pstateToSave)))) { + if (!isNotRendered && inDrawableRange) { + //conditions where visibility is assured + val durationSince = now - lastTime //ms + lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange lazy val targetDelay = { val populationOver = math.max( 0, @@ -102,12 +99,17 @@ class SessionAvatarHandlers( case index => drawConfig.delays(index) } } //ms - if (comingIntoVisibleRange || - canSeeReallyFar || - currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || - durationSince > drawConfig.delayMax || - sessionData.canSeeReallyFar || - durationSince > targetDelay) { + if (!wasVisible || + !previouslyInDrawableRange || + (!lastMsg.contains(pstateToSave) && + (canSeeReallyFar || + currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || + durationSince > drawConfig.delayMax || + sessionData.canSeeReallyFar || + durationSince > targetDelay + ) + ) + ) { //must draw sendResponse( PlayerStateMessage( @@ -126,28 +128,31 @@ class SessionAvatarHandlers( ) lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, now) } else { + //is visible, but skip reinforcement + lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, lastTime) + } + } else { + //conditions where the target is not currently visible + if (wasVisible) { + //the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance + val lat = (1 + hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat + sendResponse( + PlayerStateMessage( + guid, + Vector3(1f, lat, 1f), + vel=None, + facingYaw=0f, + facingPitch=0f, + facingYawUpper=0f, + timestamp=0, //is this okay? + is_cloaked = isCloaking + ) + ) + lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, now) + } else { + //skip drawing altogether lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, lastTime) } - } else if ( - (!spectating && wasInVisibleRange && !inVisibleRange) || - (inVisibleRange && !wasSpectating && spectating) || - (wasVisible && released) - ) { - //must hide - val lat = (1 + hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat - sendResponse( - PlayerStateMessage( - guid, - Vector3(1f, lat, 1f), - vel=None, - facingYaw=0f, - facingPitch=0f, - facingYawUpper=0f, - timestamp=0, //is this okay? - is_cloaked = isCloaking - ) - ) - lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, now) } case AvatarResponse.ObjectHeld(slot, _) @@ -157,6 +162,9 @@ class SessionAvatarHandlers( continent.GUID(sessionData.terminals.usingMedicalTerminal).collect { case term: Terminal with ProximityUnit => sessionData.terminals.StopUsingProximityUnit(term) } + if (sessionData.zoning.zoningStatus == Zoning.Status.Deconstructing) { + sessionData.zoning.zoningStatus = Zoning.Status.None + } case AvatarResponse.ObjectHeld(slot, _) if isSameTarget && slot > -1 => diff --git a/src/main/scala/net/psforever/actors/session/support/SessionData.scala b/src/main/scala/net/psforever/actors/session/support/SessionData.scala index 0ad74b3d9..9214cf129 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionData.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionData.scala @@ -210,7 +210,11 @@ class SessionData( val isMoving = WorldEntity.isMoving(vel) val isMovingPlus = isMoving || isJumping || jumpThrust if (isMovingPlus) { - zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion") + if (zoning.zoningStatus == Zoning.Status.Deconstructing) { + zoning.zoningStatus = Zoning.Status.None + } else { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion") + } } fallHeightTracker(pos.z) // if (isCrouching && !player.Crouching) { @@ -258,6 +262,9 @@ class SessionData( case None => () } val eagleEye: Boolean = canSeeReallyFar + val isNotVisible: Boolean = player.spectator || + zoning.zoningStatus == Zoning.Status.Deconstructing || + (player.isAlive && zoning.spawn.deadState == DeadState.RespawnTime) continent.AvatarEvents ! AvatarServiceMessage( continent.id, AvatarAction.PlayerState( @@ -272,7 +279,7 @@ class SessionData( isJumping, jumpThrust, isCloaking, - player.spectator, + isNotVisible, eagleEye ) ) @@ -516,15 +523,27 @@ class SessionData( def handleAvatarImplant(pkt: AvatarImplantMessage): Unit = { val AvatarImplantMessage(_, action, slot, status) = pkt if (action == ImplantAction.Activation) { - zoning.CancelZoningProcessWithDescriptiveReason("cancel_implant") - avatar.implants(slot) match { - case Some(implant) => - if (status == 1) { - avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType) - } else { + if (zoning.zoningStatus == Zoning.Status.Deconstructing) { + //do not activate; play deactivation sound instead + zoning.zoningStatus == Zoning.Status.None + avatar.implants(slot).collect { + case implant if implant.active => avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType) - } - case _ => log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot") + case implant => + sendResponse(PlanetsideAttributeMessage(player.GUID, 28, implant.definition.implantType.value * 2)) + } + } else { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_implant") + avatar.implants(slot) match { + case Some(implant) => + if (status == 1) { + avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType) + } else { + avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType) + } + case _ => + log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot") + } } } } @@ -1425,6 +1444,12 @@ class SessionData( log.info(s"${player.Name} is deconstructing at the ${obj.Owner.Definition.Name}'s spawns") zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") playerActionsToCancel() + avatar.implants.collect { + case Some(implant) if implant.active && !implant.definition.Passive => + avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType) + } + player.Actor ! PlayerControl.ObjectHeld(Player.HandsDownSlot, updateMyHolsterArm = true) + zoning.zoningStatus = Zoning.Status.Deconstructing terminals.CancelAllProximityUnits() zoning.spawn.GoToDeploymentMap() case _ => () diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala index fa576f638..417a8342d 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -1920,6 +1920,7 @@ class ZoningOperations( TurnCounterDuringInterim } sessionData.keepAliveFunc = NormalKeepAlive + zoningStatus = Zoning.Status.None upstreamMessageCount = 0 setAvatar = false sessionData.persist() @@ -1950,6 +1951,7 @@ class ZoningOperations( TurnCounterDuringInterim } sessionData.keepAliveFunc = NormalKeepAlive + zoningStatus = Zoning.Status.None upstreamMessageCount = 0 setAvatar = false sessionData.persist() diff --git a/src/main/scala/net/psforever/objects/zones/Zoning.scala b/src/main/scala/net/psforever/objects/zones/Zoning.scala index 6d9bcb7fc..54b256606 100644 --- a/src/main/scala/net/psforever/objects/zones/Zoning.scala +++ b/src/main/scala/net/psforever/objects/zones/Zoning.scala @@ -25,6 +25,7 @@ object Zoning { val values: IndexedSeq[Status] = findValues case object None extends Status(value = "None") + case object Deconstructing extends Status(value = "Deconstructing") case object Request extends Status(value = "Request") case object Countdown extends Status(value = "Countdown") }