mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Fixes
* RefreshLoadouts no longer queries loadoads from the database * /gmtell no longer sends to self * Set AMS spawn timer to 10 seconds * Fix medkit cooldown display * Fix wrong max type being purchased * Fix implants not being locked when fatigued * Fix implants progress bar * Make tells case insensitive
This commit is contained in:
parent
63dea5af05
commit
e34f96ce18
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<img src="https://psforever.net/index_files/logo_crop.png" align="left" title="PSForever" width="120">
|
||||
|
||||
Welcome to the recreated login and world servers for PlanetSide 1. We are a awesome community of players and developers who took
|
||||
Welcome to the recreated login and world servers for PlanetSide 1. We are a community of players and developers who took
|
||||
it upon ourselves to preserve PlanetSide 1's unique gameplay and history _forever_.
|
||||
|
||||
The login and world servers (this repo runs both by default) are built to work with PlanetSide version 3.15.84.0.
|
||||
|
|
|
|||
|
|
@ -663,7 +663,13 @@ class AvatarActor(
|
|||
case LoadoutType.Infantry =>
|
||||
storeLoadout(player, name, number).onComplete {
|
||||
case Success(_) =>
|
||||
context.self ! RefreshLoadouts()
|
||||
loadLoadouts().onComplete {
|
||||
case Success(loadouts) =>
|
||||
avatar = avatar.copy(loadouts = loadouts)
|
||||
context.self ! RefreshLoadouts()
|
||||
case Failure(exception) => log.error(exception)("db failure")
|
||||
}
|
||||
|
||||
case Failure(exception) => log.error(exception)("db failure")
|
||||
}
|
||||
|
||||
|
|
@ -693,23 +699,18 @@ class AvatarActor(
|
|||
Behaviors.same
|
||||
|
||||
case RefreshLoadouts() =>
|
||||
loadLoadouts().onComplete {
|
||||
case Success(loadouts) =>
|
||||
avatar = avatar.copy(loadouts = loadouts)
|
||||
loadouts.zipWithIndex.foreach {
|
||||
case (Some(loadout: InfantryLoadout), index) =>
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
FavoritesMessage(
|
||||
LoadoutType.Infantry,
|
||||
session.get.player.GUID,
|
||||
index,
|
||||
loadout.label,
|
||||
InfantryLoadout.DetermineSubtypeB(loadout.exosuit, loadout.subtype)
|
||||
)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
case Failure(exception) => log.error(exception)("db failure")
|
||||
avatar.loadouts.zipWithIndex.foreach {
|
||||
case (Some(loadout: InfantryLoadout), index) =>
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
FavoritesMessage(
|
||||
LoadoutType.Infantry,
|
||||
session.get.player.GUID,
|
||||
index,
|
||||
loadout.label,
|
||||
InfantryLoadout.DetermineSubtypeB(loadout.exosuit, loadout.subtype)
|
||||
)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
Behaviors.same
|
||||
|
||||
|
|
@ -772,7 +773,6 @@ class AvatarActor(
|
|||
Behaviors.same
|
||||
|
||||
case ActivateImplant(implantType) =>
|
||||
log.info(s"ActivateImplant ${implantType}")
|
||||
val res = avatar.implants.zipWithIndex.collectFirst {
|
||||
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
|
||||
}
|
||||
|
|
@ -780,14 +780,14 @@ class AvatarActor(
|
|||
case Some((implant, slot)) =>
|
||||
if (!implant.initialized) {
|
||||
log.error(s"requested activation of uninitialized implant $implant")
|
||||
} else if (!consumeStamina(implant.definition.ActivationStaminaCost)) {
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.OutOfStamina, slot, 1)
|
||||
)
|
||||
} else if (
|
||||
!consumeStamina(implant.definition.ActivationStaminaCost) ||
|
||||
avatar.stamina < implant.definition.StaminaCost
|
||||
) {
|
||||
// not enough stamina to activate
|
||||
} else if (implant.definition.implantType.disabledFor.contains(session.get.player.ExoSuit)) {
|
||||
// TODO can this really happen? can we prevent it?
|
||||
} else {
|
||||
|
||||
avatar = avatar.copy(
|
||||
implants = avatar.implants.updated(slot, Some(implant.copy(active = true)))
|
||||
)
|
||||
|
|
@ -828,39 +828,15 @@ class AvatarActor(
|
|||
Behaviors.same
|
||||
|
||||
case DeactivateImplant(implantType) =>
|
||||
val res = avatar.implants.zipWithIndex.collectFirst {
|
||||
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
|
||||
}
|
||||
res match {
|
||||
case Some((implant, slot)) =>
|
||||
implantTimers(slot).cancel()
|
||||
avatar = avatar.copy(
|
||||
implants = avatar.implants.updated(slot, Some(implant.copy(active = false)))
|
||||
)
|
||||
|
||||
// Deactivation sound / effect
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
session.get.zone.id,
|
||||
AvatarAction.PlanetsideAttribute(session.get.player.GUID, 28, implant.definition.implantType.value * 2)
|
||||
)
|
||||
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(
|
||||
session.get.player.GUID,
|
||||
ImplantAction.Activation,
|
||||
slot,
|
||||
0
|
||||
)
|
||||
)
|
||||
case None => log.error(s"requested deactivation of unknown implant $implantType")
|
||||
}
|
||||
deactivateImplant(implantType)
|
||||
Behaviors.same
|
||||
|
||||
case DeactivateActiveImplants() =>
|
||||
avatar.implants.indices.foreach { index =>
|
||||
avatar.implants(index).foreach { implant =>
|
||||
if (implant.active && implant.definition.GetCostIntervalByExoSuit(session.get.player.ExoSuit) > 0)
|
||||
context.self ! DeactivateImplant(implant.definition.implantType)
|
||||
if (implant.active && implant.definition.GetCostIntervalByExoSuit(session.get.player.ExoSuit) > 0) {
|
||||
deactivateImplant(implant.definition.implantType)
|
||||
}
|
||||
}
|
||||
}
|
||||
Behaviors.same
|
||||
|
|
@ -870,13 +846,18 @@ class AvatarActor(
|
|||
if (session.get.player.HasGUID) {
|
||||
val totalStamina = math.min(avatar.maxStamina, avatar.stamina + stamina)
|
||||
val fatigued = if (avatar.fatigued && totalStamina >= 20) {
|
||||
context.self ! InitializeImplants(instant = true)
|
||||
avatar.implants.zipWithIndex.foreach {
|
||||
case (Some(implant), 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)
|
||||
)
|
||||
|
|
@ -1027,6 +1008,19 @@ class AvatarActor(
|
|||
} else {
|
||||
totalStamina == 0
|
||||
}
|
||||
if (!avatar.fatigued && fatigued) {
|
||||
avatar.implants.zipWithIndex.foreach {
|
||||
case (Some(implant), slot) =>
|
||||
if (implant.active) {
|
||||
deactivateImplant(implant.definition.implantType)
|
||||
}
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.OutOfStamina, slot, 1)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
avatar = avatar.copy(stamina = totalStamina, fatigued = fatigued)
|
||||
sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 2, avatar.stamina))
|
||||
consumed
|
||||
|
|
@ -1047,6 +1041,14 @@ class AvatarActor(
|
|||
)
|
||||
)
|
||||
|
||||
// Start client side initialization timer, visible on the character screen
|
||||
// Progress accumulates according to the client's knowledge of the implant initialization time
|
||||
// What is normally a 60s timer that is set to 120s on the server will still visually update as if 60s
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
avatar.name,
|
||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
|
||||
)
|
||||
|
||||
implantTimers.get(slot).foreach(_.cancel())
|
||||
implantTimers(slot) = context.system.scheduler.scheduleOnce(
|
||||
if (instant) 0.seconds else implant.definition.InitializationDuration.seconds,
|
||||
|
|
@ -1084,6 +1086,35 @@ class AvatarActor(
|
|||
})
|
||||
}
|
||||
|
||||
def deactivateImplant(implantType: ImplantType): Unit = {
|
||||
val res = avatar.implants.zipWithIndex.collectFirst {
|
||||
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
|
||||
}
|
||||
res match {
|
||||
case Some((implant, slot)) =>
|
||||
implantTimers(slot).cancel()
|
||||
avatar = avatar.copy(
|
||||
implants = avatar.implants.updated(slot, Some(implant.copy(active = false)))
|
||||
)
|
||||
|
||||
// Deactivation sound / effect
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
session.get.zone.id,
|
||||
AvatarAction.PlanetsideAttribute(session.get.player.GUID, 28, implant.definition.implantType.value * 2)
|
||||
)
|
||||
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(
|
||||
session.get.player.GUID,
|
||||
ImplantAction.Activation,
|
||||
slot,
|
||||
0
|
||||
)
|
||||
)
|
||||
case None => log.error(s"requested deactivation of unknown implant $implantType")
|
||||
}
|
||||
}
|
||||
|
||||
/** Send list of avatars to client (show character selection screen) */
|
||||
def sendAvatars(account: Account): Unit = {
|
||||
import ctx._
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ class ChatActor(
|
|||
case (CMT_GMTELL, _, _) if gmCommandAllowed =>
|
||||
chatService ! ChatService.Message(
|
||||
session,
|
||||
message.copy(recipient = session.player.Name),
|
||||
message,
|
||||
ChatChannel.Default()
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -90,18 +90,6 @@ import akka.util.Timeout
|
|||
import scala.collection.mutable
|
||||
|
||||
object SessionActor {
|
||||
|
||||
/** Object use cooldowns.<br>
|
||||
* key - object id<br>
|
||||
* value - time last used (ms)
|
||||
*/
|
||||
val delayedGratificationEntries: Map[Int, Long] = Map(
|
||||
GlobalDefinitions.medkit.ObjectId -> 5000, //5s
|
||||
GlobalDefinitions.super_armorkit.ObjectId -> 1200000, //20min
|
||||
GlobalDefinitions.super_medkit.ObjectId -> 1200000, //20min
|
||||
GlobalDefinitions.super_staminakit.ObjectId -> 1200000 //20min
|
||||
)
|
||||
|
||||
sealed trait Command
|
||||
|
||||
final case class ResponseToSelf(pkt: PlanetSideGamePacket)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import org.json4s._
|
|||
import org.json4s.native.Serialization.write
|
||||
import scodec.bits._
|
||||
import scodec.interop.akka._
|
||||
import net.psforever.services.ServiceManager.Lookup
|
||||
import net.psforever.services._
|
||||
import scala.collection.mutable.Map
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
|
|
|
|||
|
|
@ -6528,7 +6528,7 @@ object GlobalDefinitions {
|
|||
*/
|
||||
private def initMiscellaneous(): Unit = {
|
||||
ams_respawn_tube.Name = "ams_respawn_tube"
|
||||
ams_respawn_tube.Delay = 5
|
||||
ams_respawn_tube.Delay = 10
|
||||
ams_respawn_tube.SpecificPointFunc = SpawnPoint.AMS
|
||||
ams_respawn_tube.Damageable = false
|
||||
ams_respawn_tube.Repairable = false
|
||||
|
|
|
|||
|
|
@ -110,10 +110,9 @@ case class Avatar(
|
|||
times.get(definition.Name) match {
|
||||
case Some(purchaseTime) =>
|
||||
val secondsSincePurchase = Seconds.secondsBetween(purchaseTime, LocalDateTime.now())
|
||||
val duration = secondsSincePurchase.toStandardDuration
|
||||
cooldowns.get(definition) match {
|
||||
case Some(cooldown) if (cooldown.toSeconds - secondsSincePurchase.getSeconds) > 0 =>
|
||||
Some(duration)
|
||||
Some(Seconds.seconds((cooldown.toSeconds - secondsSincePurchase.getSeconds).toInt).toStandardDuration)
|
||||
case _ => None
|
||||
}
|
||||
case None =>
|
||||
|
|
|
|||
|
|
@ -358,12 +358,13 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
|||
val originalArmor = player.Armor
|
||||
player.ExoSuit = nextSuit
|
||||
val toMaxArmor = player.MaxArmor
|
||||
val toArmor = if (originalSuit != nextSuit || originalSubtype != nextSubtype || originalArmor > toMaxArmor) {
|
||||
player.History(HealFromExoSuitChange(PlayerSource(player), nextSuit))
|
||||
player.Armor = toMaxArmor
|
||||
} else {
|
||||
player.Armor = originalArmor
|
||||
}
|
||||
val toArmor =
|
||||
if (originalSuit != nextSuit || originalSubtype != nextSubtype || originalArmor > toMaxArmor) {
|
||||
player.History(HealFromExoSuitChange(PlayerSource(player), nextSuit))
|
||||
player.Armor = toMaxArmor
|
||||
} else {
|
||||
player.Armor = originalArmor
|
||||
}
|
||||
//ensure arm is down, even if it needs to go back up
|
||||
if (player.DrawnSlot != Player.HandsDownSlot) {
|
||||
player.DrawnSlot = Player.HandsDownSlot
|
||||
|
|
|
|||
|
|
@ -31,15 +31,15 @@ object EquipmentTerminalDefinition {
|
|||
* value - a `Tuple` containing exo-suit specifications
|
||||
*/
|
||||
val maxSuits: Map[String, (ExoSuitType.Value, Int)] = Map(
|
||||
"trhev_antiaircraft" -> (ExoSuitType.MAX, 3),
|
||||
"trhev_antipersonnel" -> (ExoSuitType.MAX, 1),
|
||||
"trhev_antivehicular" -> (ExoSuitType.MAX, 2),
|
||||
"nchev_antiaircraft" -> (ExoSuitType.MAX, 3),
|
||||
"nchev_antipersonnel" -> (ExoSuitType.MAX, 1),
|
||||
"nchev_antivehicular" -> (ExoSuitType.MAX, 2),
|
||||
"vshev_antiaircraft" -> (ExoSuitType.MAX, 3),
|
||||
"vshev_antipersonnel" -> (ExoSuitType.MAX, 1),
|
||||
"vshev_antivehicular" -> (ExoSuitType.MAX, 2)
|
||||
"trhev_antiaircraft" -> (ExoSuitType.MAX, 1),
|
||||
"trhev_antipersonnel" -> (ExoSuitType.MAX, 2),
|
||||
"trhev_antivehicular" -> (ExoSuitType.MAX, 3),
|
||||
"nchev_antiaircraft" -> (ExoSuitType.MAX, 1),
|
||||
"nchev_antipersonnel" -> (ExoSuitType.MAX, 2),
|
||||
"nchev_antivehicular" -> (ExoSuitType.MAX, 3),
|
||||
"vshev_antiaircraft" -> (ExoSuitType.MAX, 1),
|
||||
"vshev_antipersonnel" -> (ExoSuitType.MAX, 2),
|
||||
"vshev_antivehicular" -> (ExoSuitType.MAX, 3)
|
||||
)
|
||||
|
||||
import net.psforever.objects.GlobalDefinitions._
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ object ImplantAction extends Enumeration {
|
|||
* `Initialization` - 0 to revoke slot; 1 to allocate implant slot<br>
|
||||
* `Activation` - 0 to deactivate implant; 1 to activate implant<br>
|
||||
* `UnlockMessage` - 0-3 as an unlocked implant slot; display a message<br>
|
||||
* `OutOfStamina` - lock implant; 0 to lock; 1 to unlock; display a message
|
||||
* `OutOfStamina` - lock implant; 1 to lock; 0 to unlock; display a message
|
||||
*/
|
||||
final case class AvatarImplantMessage(
|
||||
player_guid: PlanetSideGUID,
|
||||
|
|
|
|||
Loading…
Reference in a new issue