mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Stamkit Fixed, Maybe, Sorta, Not Really, Take Two (#966)
* fixed stamina kit, maybe * ui field updates for stamina on kit use
This commit is contained in:
parent
a8630e11ab
commit
46763b7877
|
|
@ -151,7 +151,10 @@ object AvatarActor {
|
|||
/** Set the avatar's lookingForSquad */
|
||||
final case class SetLookingForSquad(lfs: Boolean) extends Command
|
||||
|
||||
/** Restore up to the given stamina amount */
|
||||
/** Restore up to the given stamina amount due to natural recharge */
|
||||
private case class RestoreStaminaPeriodically(stamina: Int) extends Command
|
||||
|
||||
/** Restore up to the given stamina amount for some reason */
|
||||
final case class RestoreStamina(stamina: Int) extends Command
|
||||
|
||||
/** Consume up to the given stamina amount */
|
||||
|
|
@ -201,7 +204,7 @@ class AvatarActor(
|
|||
var account: Option[Account] = None
|
||||
var session: Option[Session] = None
|
||||
val implantTimers: mutable.Map[Int, Cancellable] = mutable.Map()
|
||||
var staminaRegenTimer: Cancellable = Cancellable.alreadyCancelled
|
||||
var staminaRegenTimer: Cancellable = Default.Cancellable
|
||||
var _avatar: Option[Avatar] = None
|
||||
var saveLockerFunc: () => Unit = storeNewLocker
|
||||
//val topic: ActorRef[Topic.Command[Avatar]] = context.spawnAnonymous(Topic[Avatar]("avatar"))
|
||||
|
|
@ -257,7 +260,7 @@ class AvatarActor(
|
|||
Behaviors.same
|
||||
|
||||
case SetLookingForSquad(lfs) =>
|
||||
avatar = avatar.copy(lookingForSquad = lfs)
|
||||
avatarCopy(avatar.copy(lookingForSquad = lfs))
|
||||
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 53, 0))
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
avatar.faction.toString,
|
||||
|
|
@ -366,16 +369,15 @@ class AvatarActor(
|
|||
|
||||
result.onComplete {
|
||||
case Success((loadouts, implants, certs, locker)) =>
|
||||
avatar = avatar.copy(
|
||||
avatarCopy(avatar.copy(
|
||||
loadouts = loadouts,
|
||||
// make sure we always have the base certifications
|
||||
certifications =
|
||||
certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
|
||||
implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
|
||||
locker = locker
|
||||
)
|
||||
staminaRegenTimer.cancel()
|
||||
staminaRegenTimer = defaultStaminaRegen()
|
||||
))
|
||||
defaultStaminaRegen(initialDelay = 0.5f seconds)
|
||||
replyTo ! AvatarLoginResponse(avatar)
|
||||
case Failure(e) =>
|
||||
log.error(e)("db failure")
|
||||
|
|
@ -385,11 +387,11 @@ class AvatarActor(
|
|||
case ReplaceAvatar(newAvatar) =>
|
||||
replaceAvatar(newAvatar)
|
||||
staminaRegenTimer.cancel()
|
||||
staminaRegenTimer = defaultStaminaRegen()
|
||||
defaultStaminaRegen(initialDelay = 0.5f seconds)
|
||||
Behaviors.same
|
||||
|
||||
case AddFirstTimeEvent(event) =>
|
||||
avatar = avatar.copy(firstTimeEvents = avatar.firstTimeEvents ++ Set(event))
|
||||
avatarCopy(avatar.copy(firstTimeEvents = avatar.firstTimeEvents ++ Set(event)))
|
||||
Behaviors.same
|
||||
|
||||
case LearnCertification(terminalGuid, certification) =>
|
||||
|
|
@ -710,7 +712,7 @@ class AvatarActor(
|
|||
}
|
||||
result.onComplete {
|
||||
case Success(_) =>
|
||||
replaceAvatar(avatar.copy(loadouts = avatar.loadouts.updated(lineNo, None)))
|
||||
avatarCopy(avatar.copy(loadouts = avatar.loadouts.updated(lineNo, None)))
|
||||
sessionActor ! SessionActor.SendResponse(FavoritesMessage(loadoutType, player.GUID, number, ""))
|
||||
case Failure(exception) =>
|
||||
log.error(exception)("db failure (?)")
|
||||
|
|
@ -742,14 +744,14 @@ class AvatarActor(
|
|||
case _ => ;
|
||||
}
|
||||
}
|
||||
avatar = avatar.copy(purchaseTimes = newTimes)
|
||||
avatarCopy(avatar.copy(purchaseTimes = newTimes))
|
||||
Behaviors.same
|
||||
|
||||
case UpdateUseTime(definition, time) =>
|
||||
if (!Avatar.useCooldowns.contains(definition)) {
|
||||
log.warn(s"${avatar.name} is updating a use time for item '${definition.Name}' that has no cooldown")
|
||||
}
|
||||
avatar = avatar.copy(useTimes = avatar.useTimes.updated(definition.Name, time))
|
||||
avatarCopy(avatar.copy(useTimes = avatar.useTimes.updated(definition.Name, time)))
|
||||
sessionActor ! SessionActor.UseCooldownRenewed(definition, time)
|
||||
Behaviors.same
|
||||
|
||||
|
|
@ -758,7 +760,7 @@ class AvatarActor(
|
|||
Behaviors.same
|
||||
|
||||
case SetVehicle(vehicle) =>
|
||||
avatar = avatar.copy(vehicle = vehicle)
|
||||
avatarCopy(avatar.copy(vehicle = vehicle))
|
||||
Behaviors.same
|
||||
|
||||
case ActivateImplant(implantType) =>
|
||||
|
|
@ -776,9 +778,9 @@ class AvatarActor(
|
|||
} else if (implant.definition.implantType.disabledFor.contains(session.get.player.ExoSuit)) {
|
||||
// TODO can this really happen? can we prevent it?
|
||||
} else {
|
||||
avatar = avatar.copy(
|
||||
avatarCopy(avatar.copy(
|
||||
implants = avatar.implants.updated(slot, Some(implant.copy(active = true)))
|
||||
)
|
||||
))
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(
|
||||
session.get.player.GUID,
|
||||
|
|
@ -849,11 +851,11 @@ class AvatarActor(
|
|||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, index, 1)
|
||||
)
|
||||
avatar = avatar.copy(implants = avatar.implants.map {
|
||||
avatarCopy(avatar.copy(implants = avatar.implants.map {
|
||||
case Some(implant) if implant.definition.implantType == implantType =>
|
||||
Some(implant.copy(initialized = true))
|
||||
case other => other
|
||||
})
|
||||
}))
|
||||
|
||||
case None => log.error(s"set initialized called for unknown implant $implantType")
|
||||
}
|
||||
|
|
@ -875,30 +877,29 @@ class AvatarActor(
|
|||
Behaviors.same
|
||||
|
||||
case RestoreStamina(stamina) =>
|
||||
if (stamina > 0 && session.get.player.HasGUID) {
|
||||
val totalStamina = math.min(avatar.maxStamina, avatar.stamina + stamina)
|
||||
val fatigued = if (avatar.fatigued && totalStamina >= 20) {
|
||||
avatar.implants.zipWithIndex.foreach {
|
||||
case (Some(_), slot) =>
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.OutOfStamina, slot, 0)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
false
|
||||
} else {
|
||||
avatar.fatigued
|
||||
}
|
||||
avatar = avatar.copy(stamina = totalStamina, fatigued = fatigued)
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
PlanetsideAttributeMessage(session.get.player.GUID, 2, avatar.stamina)
|
||||
)
|
||||
tryRestoreStaminaForSession(stamina) match {
|
||||
case Some(sess) =>
|
||||
actuallyRestoreStamina(stamina, sess)
|
||||
defaultStaminaRegen(initialDelay = 0.5f seconds)
|
||||
case _ => ;
|
||||
}
|
||||
Behaviors.same
|
||||
|
||||
case RestoreStaminaPeriodically(stamina) =>
|
||||
tryRestoreStaminaForSession(stamina) match {
|
||||
case Some(sess) =>
|
||||
actuallyRestoreStaminaIfStationary(stamina, sess)
|
||||
case _ => ;
|
||||
}
|
||||
defaultStaminaRegen(initialDelay = 0.5f seconds)
|
||||
Behaviors.same
|
||||
|
||||
case ConsumeStamina(stamina) =>
|
||||
if (stamina > 0) {
|
||||
consumeThisMuchStamina(stamina)
|
||||
if(staminaRegenTimer.isCancelled) {
|
||||
defaultStaminaRegen(initialDelay = 0.5f seconds)
|
||||
}
|
||||
} else {
|
||||
log.warn(s"consumed stamina must be larger than 0, but is: $stamina")
|
||||
}
|
||||
|
|
@ -906,13 +907,7 @@ class AvatarActor(
|
|||
|
||||
case SuspendStaminaRegeneration(duration) =>
|
||||
// TODO suspensions can overwrite each other with different durations
|
||||
staminaRegenTimer.cancel()
|
||||
staminaRegenTimer = context.system.scheduler.scheduleOnce(
|
||||
duration,
|
||||
() => {
|
||||
staminaRegenTimer = defaultStaminaRegen()
|
||||
}
|
||||
)
|
||||
defaultStaminaRegen(duration)
|
||||
Behaviors.same
|
||||
|
||||
case InitializeImplants() =>
|
||||
|
|
@ -1018,8 +1013,13 @@ class AvatarActor(
|
|||
Future.failed(ex).asInstanceOf[Future[Loadout]]
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @see `avatarCopy(Avatar)`
|
||||
* @param newAvatar na
|
||||
*/
|
||||
def replaceAvatar(newAvatar: Avatar): Unit = {
|
||||
avatar = newAvatar
|
||||
avatarCopy(newAvatar)
|
||||
avatar.deployables.UpdateMaxCounts(avatar.certifications)
|
||||
updateDeployableUIElements(
|
||||
avatar.deployables.UpdateUI()
|
||||
|
|
@ -1038,7 +1038,7 @@ class AvatarActor(
|
|||
)
|
||||
.onComplete {
|
||||
case Success(_) =>
|
||||
avatar = avatar.copy(cosmetics = Some(cosmetics))
|
||||
avatarCopy(avatar.copy(cosmetics = Some(cosmetics)))
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
session.get.zone.id,
|
||||
AvatarAction
|
||||
|
|
@ -1052,13 +1052,45 @@ class AvatarActor(
|
|||
p.future
|
||||
}
|
||||
|
||||
def tryRestoreStaminaForSession(stamina: Int): Option[Session] = {
|
||||
session match {
|
||||
case out @ Some(_) if !avatar.staminaFull && stamina > 0 => out
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
def actuallyRestoreStaminaIfStationary(stamina: Int, session: Session): Unit = {
|
||||
if (session.player.VehicleSeated.nonEmpty || !(session.player.isMoving || session.player.Jumping)) {
|
||||
actuallyRestoreStamina(stamina, session)
|
||||
}
|
||||
}
|
||||
|
||||
def actuallyRestoreStamina(stamina: Int, session: Session): Unit = {
|
||||
val totalStamina = math.min(avatar.maxStamina, avatar.stamina + stamina)
|
||||
val isFatigued = if (avatar.fatigued && totalStamina >= 20) {
|
||||
val pguid = session.player.GUID
|
||||
avatar.implants.zipWithIndex.foreach {
|
||||
case (Some(_), slot) =>
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(pguid, ImplantAction.OutOfStamina, slot, 0)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
false
|
||||
} else {
|
||||
avatar.fatigued
|
||||
}
|
||||
avatarCopy(avatar.copy(stamina = totalStamina, fatigued = isFatigued))
|
||||
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.player.GUID, 2, totalStamina))
|
||||
}
|
||||
|
||||
/**
|
||||
* Drain at most a given amount of stamina from the player's pool of stamina.
|
||||
* If the player's reserves become zero in the act, inform the player that he is fatigued
|
||||
* meaning that he will only be able to walk, all implants will deactivate,
|
||||
* and all exertion that require stamina use will become impossible until a threshold of stamina is regained.
|
||||
* @param stamina an amount to drain
|
||||
* @return `true`, as long as the amount of stamina can be drained in total;
|
||||
* @return `true`, as long as the requested amount of stamina can be drained in total;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def consumeThisMuchStamina(stamina: Int): Boolean = {
|
||||
|
|
@ -1069,7 +1101,7 @@ class AvatarActor(
|
|||
val totalStamina = math.max(0, resultingStamina)
|
||||
val alreadyFatigued = avatar.fatigued
|
||||
val becomeFatigued = !alreadyFatigued && totalStamina == 0
|
||||
avatar = avatar.copy(stamina = totalStamina, fatigued = alreadyFatigued || becomeFatigued)
|
||||
avatarCopy(avatar.copy(stamina = totalStamina, fatigued = alreadyFatigued || becomeFatigued))
|
||||
val player = session.get.player
|
||||
if (player.HasGUID) {
|
||||
if (becomeFatigued) {
|
||||
|
|
@ -1084,15 +1116,15 @@ class AvatarActor(
|
|||
case _ => ;
|
||||
}
|
||||
}
|
||||
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(player.GUID, 2, avatar.stamina))
|
||||
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(player.GUID, 2, totalStamina))
|
||||
} else if (becomeFatigued) {
|
||||
avatar = avatar.copy(implants = avatar.implants.zipWithIndex.collect {
|
||||
avatarCopy(avatar.copy(implants = avatar.implants.zipWithIndex.collect {
|
||||
case (Some(implant), slot) if implant.active =>
|
||||
implantTimers(slot).cancel()
|
||||
Some(implant.copy(active = false))
|
||||
case (out, _) =>
|
||||
out
|
||||
})
|
||||
}))
|
||||
}
|
||||
resultingStamina >= 0
|
||||
}
|
||||
|
|
@ -1133,7 +1165,7 @@ class AvatarActor(
|
|||
}
|
||||
|
||||
def deinitializeImplants(): Unit = {
|
||||
avatar = avatar.copy(implants = avatar.implants.zipWithIndex.map {
|
||||
avatarCopy(avatar.copy(implants = avatar.implants.zipWithIndex.map {
|
||||
case (Some(implant), slot) =>
|
||||
if (implant.active) {
|
||||
deactivateImplant(implant.definition.implantType)
|
||||
|
|
@ -1147,7 +1179,7 @@ class AvatarActor(
|
|||
)
|
||||
Some(implant.copy(initialized = false, active = false))
|
||||
case (None, _) => None
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
def resetAnImplant(implantType: ImplantType): Unit = {
|
||||
|
|
@ -1168,9 +1200,9 @@ class AvatarActor(
|
|||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, index, 0)
|
||||
)
|
||||
)
|
||||
avatar = avatar.copy(
|
||||
avatarCopy(avatar.copy(
|
||||
implants = avatar.implants.updated(index, Some(imp.copy(initialized = false, active = false)))
|
||||
)
|
||||
))
|
||||
//restart initialization process
|
||||
implantTimers(index).cancel()
|
||||
implantTimers(index) = context.scheduleOnce(
|
||||
|
|
@ -1192,9 +1224,9 @@ class AvatarActor(
|
|||
} match {
|
||||
case Some((implant, slot)) =>
|
||||
implantTimers(slot).cancel()
|
||||
avatar = avatar.copy(
|
||||
avatarCopy(avatar.copy(
|
||||
implants = avatar.implants.updated(slot, Some(implant.copy(active = false)))
|
||||
)
|
||||
))
|
||||
|
||||
// Deactivation sound / effect
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
|
|
@ -1647,18 +1679,15 @@ class AvatarActor(
|
|||
}
|
||||
}
|
||||
|
||||
def defaultStaminaRegen(): Cancellable = {
|
||||
context.system.scheduler.scheduleWithFixedDelay(0.5 seconds, 0.5 seconds)(() => {
|
||||
(session, _avatar) match {
|
||||
case (Some(_session), Some(_)) =>
|
||||
if (
|
||||
!avatar.staminaFull && (_session.player.VehicleSeated.nonEmpty || !_session.player.isMoving && !_session.player.Jumping)
|
||||
) {
|
||||
context.self ! RestoreStamina(1)
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
})
|
||||
def defaultStaminaRegen(initialDelay: FiniteDuration): Unit = {
|
||||
staminaRegenTimer.cancel()
|
||||
staminaRegenTimer = if (!avatar.staminaFull) {
|
||||
context.system.scheduler.scheduleWithFixedDelay(initialDelay, 0.5 seconds)(() => {
|
||||
context.self ! RestoreStaminaPeriodically(1)
|
||||
})
|
||||
} else {
|
||||
Default.Cancellable
|
||||
}
|
||||
}
|
||||
|
||||
// same as in SA, this really doesn't belong here
|
||||
|
|
@ -1731,7 +1760,7 @@ class AvatarActor(
|
|||
}
|
||||
}
|
||||
if (keysToDrop.nonEmpty) {
|
||||
avatar = avatar.copy(purchaseTimes = avatar.purchaseTimes.removedAll(keysToDrop))
|
||||
avatarCopy(avatar.copy(purchaseTimes = avatar.purchaseTimes.removedAll(keysToDrop)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1741,4 +1770,18 @@ class AvatarActor(
|
|||
AvatarVehicleTimerMessage(session.get.player.GUID, name, time, unk1 = true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @see `replaceCopy(Avatar)`
|
||||
* @param copyAvatar na
|
||||
*/
|
||||
def avatarCopy(copyAvatar: Avatar): Unit = {
|
||||
avatar = copyAvatar
|
||||
session match {
|
||||
case Some(sess) if sess.player != null =>
|
||||
sess.player.avatar = copyAvatar
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,8 +265,8 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
(None, 0, 0, "Stamina at maximum - No recharge required.")
|
||||
} else {
|
||||
avatarActor ! AvatarActor.RestoreStamina(100)
|
||||
//(Some(index), 2, player.avatar.stamina, "")
|
||||
(None, 0, 0, "")
|
||||
//proper stamina update will occur due to above message; update something relatively harmless instead
|
||||
(Some(index), 3, player.avatar.maxStamina, "")
|
||||
}
|
||||
} else {
|
||||
log.warn(s"UseItem: Your $kit behavior is not supported, ${player.Name}")
|
||||
|
|
|
|||
Loading…
Reference in a new issue