You Can (Not) Hold (#1034)

* dropping the medical applicator on rejoin logins; not actually a bug fix, but a bug mitigation

* narrowing the scope of the guard boolean; correcting an issue with purchase timers
This commit is contained in:
Fate-JH 2023-02-23 00:26:40 -05:00 committed by GitHub
parent 690d7ec948
commit 0b8ff5a4ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 55 deletions

View file

@ -1446,9 +1446,8 @@ class AvatarActor(
name,
cooldown.toSeconds,
item match {
case t: ToolDefinition => GlobalDefinitions.isMaxArms(t)
case _: VehicleDefinition => true
case _ => false
case _: KitDefinition => false
case _ => true
}
)
case _ => ;
@ -2573,9 +2572,8 @@ class AvatarActor(
name,
cooldown.toSeconds - secondsSincePurchase,
obj match {
case t: ToolDefinition => GlobalDefinitions.isMaxArms(t)
case _: VehicleDefinition => true
case _ => false
case _: KitDefinition => false
case _ => true
}
)
@ -2591,9 +2589,9 @@ class AvatarActor(
}
}
def updatePurchaseTimer(name: String, time: Long, isActuallyAMachine: Boolean): Unit = {
def updatePurchaseTimer(name: String, time: Long, isNotAMedKit: Boolean): Unit = {
sessionActor ! SessionActor.SendResponse(
AvatarVehicleTimerMessage(session.get.player.GUID, name, time, isActuallyAMachine)
AvatarVehicleTimerMessage(session.get.player.GUID, name, time, isNotAMedKit)
)
}

View file

@ -6,6 +6,8 @@ import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
import akka.pattern.ask
import akka.util.Timeout
import net.psforever.login.WorldSession
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.mount.Seat
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
@ -559,19 +561,15 @@ class ZoningOperations(
zone.Buildings.foreach({ case (_, building) => initBuilding(continentNumber, building.MapId, building) })
sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
//TODO should actually not claim that the sanctuary or VR zones are locked by their respective empire
if (continentNumber == 11)
sendResponse(
ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NC)
) // "The NC have captured the NC Sanctuary."
sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NC))
else if (continentNumber == 12)
sendResponse(
ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.TR)
) // "The TR have captured the TR Sanctuary."
sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.TR))
else if (continentNumber == 13)
sendResponse(
ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.VS)
) // "The VS have captured the VS Sanctuary."
else sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.VS))
else
sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
//CaptureFlagUpdateMessage()
//VanuModuleUpdateMessage()
//ModuleLimitsMessage()
@ -1804,6 +1802,7 @@ class ZoningOperations(
session = session.copy(player = p, avatar = a)
sessionData.persist()
setupAvatarFunc = AvatarRejoin
dropMedicalApplicators(p)
avatarActor ! AvatarActor.ReplaceAvatar(a)
avatarLoginResponse(a)
@ -1813,6 +1812,7 @@ class ZoningOperations(
deadState = DeadState.Dead
session = session.copy(player = p, avatar = a)
sessionData.persist()
dropMedicalApplicators(p)
HandleReleaseAvatar(p, inZone)
avatarActor ! AvatarActor.ReplaceAvatar(a)
avatarLoginResponse(a)
@ -1894,8 +1894,12 @@ class ZoningOperations(
}
def handleNewPlayerLoaded(tplayer: Player): Unit = {
//new zone
log.info(s"${tplayer.Name} has spawned into ${session.zone.id}")
/* new zone, might be on `tplayer.Zone` but should definitely be on `session` */
val zone = session.zone
val id = zone.id
val map = zone.map
val mapName = map.name
log.info(s"${tplayer.Name} has spawned into $id")
sessionData.oldRefsMap.clear()
sessionData.persist = UpdatePersistenceAndRefs
tplayer.avatar = avatar
@ -1903,18 +1907,8 @@ class ZoningOperations(
avatarActor ! AvatarActor.CreateImplants()
avatarActor ! AvatarActor.InitializeImplants()
//LoadMapMessage causes the client to send BeginZoningMessage, eventually leading to SetCurrentAvatar
val weaponsEnabled =
session.zone.map.name != "map11" && session.zone.map.name != "map12" && session.zone.map.name != "map13"
sendResponse(
LoadMapMessage(
session.zone.map.name,
session.zone.id,
40100,
25,
weaponsEnabled,
session.zone.map.checksum
)
)
val weaponsEnabled = !(mapName.equals("map11") || mapName.equals("map12") || mapName.equals("map13"))
sendResponse(LoadMapMessage(mapName, id, 40100, 25, weaponsEnabled, map.checksum))
if (isAcceptableNextSpawnPoint) {
//important! the LoadMapMessage must be processed by the client before the avatar is created
setupAvatarFunc()
@ -1934,7 +1928,7 @@ class ZoningOperations(
} else {
//look for different spawn point in same zone
cluster ! ICS.GetNearbySpawnPoint(
session.zone.Number,
zone.Number,
tplayer,
Seq(SpawnGroup.Facility, SpawnGroup.Tower, SpawnGroup.AMS),
context.self
@ -1974,6 +1968,25 @@ class ZoningOperations(
/* support functions */
private def dropMedicalApplicators(p: Player): Unit = {
WorldSession.DropLeftovers(p)(
(p.Holsters().zipWithIndex.collect { case (slot, index) if slot.Equipment.nonEmpty => InventoryItem(slot.Equipment.get, index) } ++
p.Inventory.Items ++
p.FreeHand.Equipment.flatMap { item => Some(InventoryItem(item, Player.FreeHandSlot)) }.toList)
.collect {
case entry @ InventoryItem(equipment, index)
if equipment.Definition == GlobalDefinitions.medicalapplicator && p.DrawnSlot == index =>
p.Slot(index).Equipment = None
p.DrawnSlot = Player.HandsDownSlot
entry
case entry @ InventoryItem(equipment, index)
if equipment.Definition == GlobalDefinitions.medicalapplicator =>
p.Slot(index).Equipment = None
entry
}
)
}
def isAcceptableNextSpawnPoint: Boolean = isAcceptableSpawnPoint(nextSpawnPoint)
def isAcceptableSpawnPoint(spawnPoint: SpawnPoint): Boolean = isAcceptableSpawnPoint(Some(spawnPoint))

View file

@ -959,7 +959,7 @@ object WorldSession {
* @param container the original object that contained the items
* @param drops the items to be dropped on the ground
*/
def DropLeftovers(container: PlanetSideServerObject with Container)(drops: List[InventoryItem]): Unit = {
def DropLeftovers(container: PlanetSideServerObject with Container)(drops: Iterable[InventoryItem]): Unit = {
//drop or retire
val zone = container.Zone
val pos = container.Position

View file

@ -7,10 +7,13 @@ import scodec.Codec
import scodec.codecs._
/**
* @param player_guid player guid
* @param text internal name of the item or vehicle name, e.g., medkit, fury, trhev_antipersonnel
* @param time cooldown/delay in seconds
* @param unk `true` for vehicles and max exo-suits; `false` for other items
* @param player_guid player guid
* @param text internal name of the item or vehicle name, e.g., medkit, fury, trhev_antipersonnel
* @param time cooldown/delay in seconds
* @param unk unk;
* most likely has to do with the visibility of the timer in equipment purchasing;
* `false` for kit items;
* `true` for almost everything else
*/
final case class AvatarVehicleTimerMessage(player_guid: PlanetSideGUID, text: String, time: Long, unk: Boolean)
extends PlanetSideGamePacket {

View file

@ -6,22 +6,29 @@ import scodec.Codec
import scodec.codecs._
/**
* map_name and nav_map_name should match (unless you want to be lost :))
*
* ex:
* map13 & home3 = vs sanc
* map10 & z10 = amerish
* map07 & z7 = esamir
*/
* Dispatched from server to client to instigate a zone change.
* The client should respond with a `BeginZoningMessage` packet.
* `map_name` and `zone_id` should correspond or the final product will be disorienting, even if it works.
* @param map_name designation of the physical zone;
* determines the (deployment) map screen
* @param zone_id designation of the entirety of the zone;
* determines the loading screen
* @param unk1 na;
* seems to match the initial projectile index (that can be assigned)
* @param unk2 na;
* seems to match the total number of unique projectile indices (that can be assigned) (before looping)
* @param weapons_unlocked live fire is permissible;
* restricts all actions instigated by that key bind
* @param checksum challenge number so that client can confirm server is using the correct version of this zone
*/
final case class LoadMapMessage(
map_name: String,
nav_map_name: String, // Also determines loading screen
unk1: Int,
unk2: Long,
weapons_unlocked: Boolean,
checksum: Long
) //?
extends PlanetSideGamePacket {
map_name: String,
zone_id: String, // Also determines loading screen
unk1: Int,
unk2: Long,
weapons_unlocked: Boolean,
checksum: Long
) extends PlanetSideGamePacket {
type Packet = LoadMapMessage
def opcode = GamePacketOpcode.LoadMapMessage
def encode = LoadMapMessage.encode(this)
@ -29,8 +36,8 @@ final case class LoadMapMessage(
object LoadMapMessage extends Marshallable[LoadMapMessage] {
implicit val codec: Codec[LoadMapMessage] = (
("map_name" | PacketHelpers.encodedString) :: // TODO: Implement encodedStringWithLimit
("nav_map_name" | PacketHelpers.encodedString) :: //TODO: Implement encodedStringWithLimit
("map_name" | PacketHelpers.encodedString) :: // TODO: Implement encodedStringWithLimit
("zone_id" | PacketHelpers.encodedString) :: //TODO: Implement encodedStringWithLimit
("unk1" | uint16L) ::
("unk2" | uint32L) ::
("weapons_unlocked" | bool) ::