* 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:
Jakob Gillich 2020-08-27 04:09:44 +02:00
parent 63dea5af05
commit e34f96ce18
10 changed files with 106 additions and 88 deletions

View file

@ -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.

View file

@ -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._

View file

@ -488,7 +488,7 @@ class ChatActor(
case (CMT_GMTELL, _, _) if gmCommandAllowed =>
chatService ! ChatService.Message(
session,
message.copy(recipient = session.player.Name),
message,
ChatChannel.Default()
)

View file

@ -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)

View file

@ -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._

View file

@ -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

View file

@ -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 =>

View file

@ -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

View file

@ -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._

View file

@ -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,