Merge pull request #1146 from ScrawnyRonnie/jammer

Keep the jam going & other misc fixes
This commit is contained in:
Fate-JH 2023-12-18 16:39:25 -05:00 committed by GitHub
commit c99e170a26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 133 additions and 49 deletions

View file

@ -8926,7 +8926,7 @@
"AbsY": 3742.29175, "AbsY": 3742.29175,
"AbsZ": 42.3043633, "AbsZ": 42.3043633,
"Yaw": 198.0, "Yaw": 198.0,
"GUID": 713, "GUID": 715,
"MapID": null, "MapID": null,
"IsChildObject": true "IsChildObject": true
}, },
@ -8939,7 +8939,7 @@
"AbsY": 3729.643, "AbsY": 3729.643,
"AbsZ": 42.3043633, "AbsZ": 42.3043633,
"Yaw": 288.0, "Yaw": 288.0,
"GUID": 714, "GUID": 713,
"MapID": null, "MapID": null,
"IsChildObject": true "IsChildObject": true
}, },
@ -8952,7 +8952,7 @@
"AbsY": 3729.643, "AbsY": 3729.643,
"AbsZ": 49.8043633, "AbsZ": 49.8043633,
"Yaw": 108.0, "Yaw": 108.0,
"GUID": 715, "GUID": 714,
"MapID": null, "MapID": null,
"IsChildObject": true "IsChildObject": true
}, },

View file

@ -10,7 +10,7 @@ import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill, Life, ScoreCard, SupportActivity} import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill, Life, ScoreCard, SupportActivity}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.sourcing.VehicleSource import net.psforever.objects.sourcing.VehicleSource
import net.psforever.objects.vital.InGameHistory import net.psforever.objects.vital.{InGameHistory, ReconstructionActivity}
import net.psforever.objects.vehicles.MountedWeapons import net.psforever.objects.vehicles.MountedWeapons
import net.psforever.types.{ChatMessageType, StatisticalCategory, StatisticalElement} import net.psforever.types.{ChatMessageType, StatisticalCategory, StatisticalElement}
import org.joda.time.{LocalDateTime, Seconds} import org.joda.time.{LocalDateTime, Seconds}
@ -235,7 +235,7 @@ object AvatarActor {
final case class SetStamina(stamina: Int) extends Command final case class SetStamina(stamina: Int) extends Command
private case class SetImplantInitialized(implantType: ImplantType) extends Command final case class SetImplantInitialized(implantType: ImplantType) extends Command
final case class MemberListRequest(action: MemberAction.Value, name: String) extends Command final case class MemberListRequest(action: MemberAction.Value, name: String) extends Command
@ -1588,8 +1588,68 @@ class AvatarActor(
Behaviors.same Behaviors.same
case ResetImplants() => case ResetImplants() =>
deinitializeImplants() val player = session.get.player
initializeImplants() // Get time of when you spawned after a deconstruction or zoning activity.
val lastDecon: Long = player.History.findLast {entry => entry.isInstanceOf[ReconstructionActivity]} match {
case Some(entry) => entry.time
case _ => 0L
}
// Get time of when you entered the world or respawned after death.
val lastRespawn: Long = player.History.findLast {entry => entry.isInstanceOf[SpawningActivity]} match {
case Some(entry) => entry.time
case _ => 0L
}
// You didn't die. You deconstructed or changed zones via warpgate/IA/recall.
// When you respawn after death, it does both recon and spawn activities, hence the minus 3000 to make sure
// this doesn't happen at respawn after death.
if (lastDecon - 3000 > lastRespawn) {
deinitializeImplants()
val implants = avatar.implants
implants.zipWithIndex.foreach {
case (Some(implant), slot) =>
sessionActor ! SessionActor.SendResponse(
CreateShortcutMessage(
session.get.player.GUID,
slot + 2,
Some(implant.definition.implantType.shortcut)
)
)
// If the amount of time that has passed since you entered the world or died is > how long it takes to
// initialize this implant, initialize it after 1 second.
if ((System.currentTimeMillis() / 1000) - (lastRespawn / 1000) > implant.definition.InitializationDuration) {
implantTimers.get(slot).foreach(_.cancel())
implantTimers(slot) = context.scheduleOnce(
1.seconds,
context.self,
SetImplantInitialized(implant.definition.implantType)
)
session.get.zone.AvatarEvents ! AvatarServiceMessage(
avatar.name,
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
)
}
// If the implant initialization timer hasn't quite finished, calculate a reduced timer based on last spawn activity
else {
val remainingTime = (lastRespawn / 1000).seconds - (System.currentTimeMillis() / 1000).seconds + implant.definition.InitializationDuration.seconds
implantTimers.get(slot).foreach(_.cancel())
implantTimers(slot) = context.scheduleOnce(
remainingTime,
context.self,
SetImplantInitialized(implant.definition.implantType)
)
session.get.zone.AvatarEvents ! AvatarServiceMessage(
avatar.name,
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
)
}
case (None, _) =>
}
}
// You just entered the world or died. Implants reset and timers start from scratch
else {
deinitializeImplants()
initializeImplants()
}
Behaviors.same Behaviors.same
case UpdateToolDischarge(stats) => case UpdateToolDischarge(stats) =>

View file

@ -219,7 +219,7 @@ class SessionData(
if (isMovingPlus) { if (isMovingPlus) {
if (zoning.zoningStatus == Zoning.Status.Deconstructing) { if (zoning.zoningStatus == Zoning.Status.Deconstructing) {
stopDeconstructing() stopDeconstructing()
} else { } else if (zoning.zoningStatus != Zoning.Status.None) {
zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion") zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion")
} }
} }

View file

@ -2,6 +2,7 @@
package net.psforever.actors.session.support package net.psforever.actors.session.support
import akka.actor.{ActorContext, typed} import akka.actor.{ActorContext, typed}
import net.psforever.objects.zones.Zoning
import net.psforever.objects.zones.exp.ToDatabase import net.psforever.objects.zones.exp.ToDatabase
import scala.collection.mutable import scala.collection.mutable
@ -50,7 +51,8 @@ private[support] class WeaponAndProjectileOperations(
private[support] var shotsWhileDead: Int = 0 private[support] var shotsWhileDead: Int = 0
private val projectiles: Array[Option[Projectile]] = private val projectiles: Array[Option[Projectile]] =
Array.fill[Option[Projectile]](Projectile.rangeUID - Projectile.baseUID)(None) Array.fill[Option[Projectile]](Projectile.rangeUID - Projectile.baseUID)(None)
private var zoningOpt: Option[ZoningOperations] = None
def zoning: ZoningOperations = zoningOpt.orNull
/* packets */ /* packets */
def handleWeaponFire(pkt: WeaponFireMessage): Unit = { def handleWeaponFire(pkt: WeaponFireMessage): Unit = {
@ -536,7 +538,9 @@ private[support] class WeaponAndProjectileOperations(
weaponGUID: PlanetSideGUID, weaponGUID: PlanetSideGUID,
projectileGUID: PlanetSideGUID projectileGUID: PlanetSideGUID
): (Option[PlanetSideGameObject with Container], Option[Tool]) = { ): (Option[PlanetSideGameObject with Container], Option[Tool]) = {
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_fire") if (player.ZoningRequest != Zoning.Method.None) {
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_fire")
}
if (player.isShielded) { if (player.isShielded) {
// Cancel NC MAX shield if it's active // Cancel NC MAX shield if it's active
sessionData.toggleMaxSpecialState(enable = false) sessionData.toggleMaxSpecialState(enable = false)

View file

@ -889,7 +889,7 @@ class ZoningOperations(
def beginZoningCountdown(runnable: Runnable): Unit = { def beginZoningCountdown(runnable: Runnable): Unit = {
val descriptor = zoningType.toString.toLowerCase val descriptor = zoningType.toString.toLowerCase
if (zoningStatus == Zoning.Status.Request) { if (zoningStatus == Zoning.Status.Request) {
avatarActor ! AvatarActor.DeinitializeImplants() avatarActor ! AvatarActor.DeactivateActiveImplants()
zoningStatus = Zoning.Status.Countdown zoningStatus = Zoning.Status.Countdown
val (time, origin) = ZoningStartInitialMessageAndTimer() val (time, origin) = ZoningStartInitialMessageAndTimer()
zoningCounter = time zoningCounter = time
@ -2300,18 +2300,8 @@ class ZoningOperations(
//vehicle and driver/passenger //vehicle and driver/passenger
val vdef = vehicle.Definition val vdef = vehicle.Definition
val vguid = vehicle.GUID val vguid = vehicle.GUID
val vdata = if (seat == 0) { val vdata = vdef.Packet.ConstructorData(vehicle).get
val seat = vehicle.Seats(0) sendResponse(ObjectCreateMessage(vehicle.Definition.ObjectId, vguid, vdata))
seat.unmount(player)
val _vdata = vdef.Packet.ConstructorData(vehicle).get
sendResponse(ObjectCreateMessage(vehicle.Definition.ObjectId, vguid, _vdata))
seat.mount(player)
_vdata
} else {
val _vdata = vdef.Packet.ConstructorData(vehicle).get
sendResponse(ObjectCreateMessage(vehicle.Definition.ObjectId, vguid, _vdata))
_vdata
}
Vehicles.ReloadAccessPermissions(vehicle, continent.id) Vehicles.ReloadAccessPermissions(vehicle, continent.id)
log.debug(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name}") log.debug(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name}")
log.trace(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name} - $vguid -> $vdata") log.trace(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name} - $vguid -> $vdata")
@ -2777,10 +2767,7 @@ class ZoningOperations(
} }
val originalDeadState = deadState val originalDeadState = deadState
deadState = DeadState.Alive deadState = DeadState.Alive
if (originalDeadState != DeadState.Alive) { avatarActor ! AvatarActor.ResetImplants()
avatarActor ! AvatarActor.ResetImplants()
}
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0)) sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
initializeShortcutsAndBank(guid) initializeShortcutsAndBank(guid)
//Favorites lists //Favorites lists

View file

@ -7568,7 +7568,7 @@ object GlobalDefinitions {
apc_tr.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L) apc_tr.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
apc_tr.Geometry = apcForm apc_tr.Geometry = apcForm
apc_tr.MaxCapacitor = 300 apc_tr.MaxCapacitor = 300
apc_tr.CapacitorRecharge = 10 apc_tr.CapacitorRecharge = 1
apc_tr.collision.avatarCollisionDamageMax = 300 apc_tr.collision.avatarCollisionDamageMax = 300
apc_tr.collision.xy = CollisionXYData(Array((0.1f, 1), (0.25f, 10), (0.5f, 40), (0.75f, 70), (1f, 110))) apc_tr.collision.xy = CollisionXYData(Array((0.1f, 1), (0.25f, 10), (0.5f, 40), (0.75f, 70), (1f, 110)))
apc_tr.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000))) apc_tr.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000)))
@ -7638,7 +7638,7 @@ object GlobalDefinitions {
apc_nc.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L) apc_nc.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
apc_nc.Geometry = apcForm apc_nc.Geometry = apcForm
apc_nc.MaxCapacitor = 300 apc_nc.MaxCapacitor = 300
apc_nc.CapacitorRecharge = 10 apc_nc.CapacitorRecharge = 1
apc_nc.collision.avatarCollisionDamageMax = 300 apc_nc.collision.avatarCollisionDamageMax = 300
apc_nc.collision.xy = CollisionXYData(Array((0.1f, 1), (0.25f, 10), (0.5f, 40), (0.75f, 70), (1f, 110))) apc_nc.collision.xy = CollisionXYData(Array((0.1f, 1), (0.25f, 10), (0.5f, 40), (0.75f, 70), (1f, 110)))
apc_nc.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000))) apc_nc.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000)))
@ -7708,7 +7708,7 @@ object GlobalDefinitions {
apc_vs.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L) apc_vs.UnderwaterLifespan(suffocation = 15000L, recovery = 7500L)
apc_vs.Geometry = apcForm apc_vs.Geometry = apcForm
apc_vs.MaxCapacitor = 300 apc_vs.MaxCapacitor = 300
apc_vs.CapacitorRecharge = 10 apc_vs.CapacitorRecharge = 1
apc_vs.collision.avatarCollisionDamageMax = 300 apc_vs.collision.avatarCollisionDamageMax = 300
apc_vs.collision.xy = CollisionXYData(Array((0.1f, 1), (0.25f, 10), (0.5f, 40), (0.75f, 70), (1f, 110))) apc_vs.collision.xy = CollisionXYData(Array((0.1f, 1), (0.25f, 10), (0.5f, 40), (0.75f, 70), (1f, 110)))
apc_vs.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000))) apc_vs.collision.z = CollisionZData(Array((2f, 1), (6f, 50), (10f, 300), (12f, 1000), (13f, 3000)))

View file

@ -84,7 +84,7 @@ class SensorDeployableControl(sensor: SensorDeployable)
override def StartJammeredStatus(target: Any, dur: Int): Unit = override def StartJammeredStatus(target: Any, dur: Int): Unit =
target match { target match {
case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed => case obj: PlanetSideServerObject with JammableUnit =>
val zone = obj.Zone val zone = obj.Zone
zone.LocalEvents ! LocalServiceMessage( zone.LocalEvents ! LocalServiceMessage(
zone.id, zone.id,

View file

@ -124,7 +124,7 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable)
override def StartJammeredStatus(target: Any, dur: Int): Unit = override def StartJammeredStatus(target: Any, dur: Int): Unit =
target match { target match {
case obj: PlanetSideServerObject with JammableUnit if !obj.Jammed => case obj: PlanetSideServerObject with JammableUnit =>
obj.Zone.VehicleEvents ! VehicleServiceMessage( obj.Zone.VehicleEvents ! VehicleServiceMessage(
obj.Zone.id, obj.Zone.id,
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1) VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1)

View file

@ -76,10 +76,10 @@ object Vehicles {
val factionChannel = s"${vehicle.Faction}" val factionChannel = s"${vehicle.Faction}"
(0 to 2).foreach(group => { (0 to 2).foreach(group => {
vehicle.PermissionGroup(group, empire) vehicle.PermissionGroup(group, empire)
vehicle.Zone.VehicleEvents ! VehicleServiceMessage( /*vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
factionChannel, factionChannel,
VehicleAction.SeatPermissions(Service.defaultPlayerGUID, guid, group, empire) VehicleAction.SeatPermissions(Service.defaultPlayerGUID, guid, group, empire)
) )*/
}) })
ReloadAccessPermissions(vehicle, player.Name) ReloadAccessPermissions(vehicle, player.Name)
Some(vehicle) Some(vehicle)
@ -135,10 +135,10 @@ object Vehicles {
val empire = VehicleLockState.Empire.id val empire = VehicleLockState.Empire.id
(0 to 2).foreach(group => { (0 to 2).foreach(group => {
vehicle.PermissionGroup(group, empire) vehicle.PermissionGroup(group, empire)
vehicle.Zone.VehicleEvents ! VehicleServiceMessage( /*vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
s"${vehicle.Faction}", s"${vehicle.Faction}",
VehicleAction.SeatPermissions(pguid, vguid, group, empire) VehicleAction.SeatPermissions(pguid, vguid, group, empire)
) )*/
}) })
ReloadAccessPermissions(vehicle, player.Name) ReloadAccessPermissions(vehicle, player.Name)
Some(vehicle) Some(vehicle)

View file

@ -1309,7 +1309,10 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
* @param data additional interaction information, if applicable * @param data additional interaction information, if applicable
*/ */
def doInteractingWithDeath(obj: PlanetSideServerObject, body: PieceOfEnvironment, data: Option[OxygenStateTarget]): Unit = { def doInteractingWithDeath(obj: PlanetSideServerObject, body: PieceOfEnvironment, data: Option[OxygenStateTarget]): Unit = {
suicide() player.History.findLast { entry => entry.isInstanceOf[ReconstructionActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time > 4000L => suicide()
case _ =>
}
} }
//noinspection ScalaUnusedSymbol //noinspection ScalaUnusedSymbol

View file

@ -253,7 +253,7 @@ trait JammableMountedWeapons extends JammableBehavior {
override def StartJammeredStatus(target: Any, dur: Int): Unit = { override def StartJammeredStatus(target: Any, dur: Int): Unit = {
target match { target match {
case obj: PlanetSideServerObject with MountedWeapons with JammableUnit if !obj.Jammed => case obj: PlanetSideServerObject with MountedWeapons with JammableUnit =>
JammableMountedWeaponsJammeredStatus(obj, statusCode = 1) JammableMountedWeaponsJammeredStatus(obj, statusCode = 1)
super.StartJammeredStatus(target, dur) super.StartJammeredStatus(target, dur)
case _ => ; case _ => ;

View file

@ -79,6 +79,8 @@ object DamageableMountable {
.values .values
.flatMap { _.occupant } .flatMap { _.occupant }
.collect { case player if player.isAlive => .collect { case player if player.isAlive =>
//make llu visible to others in zone if passenger is carrying one
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DropSpecialItem())
//player.LogActivity(cause) //player.LogActivity(cause)
player.Actor ! Player.Die( player.Actor ! Player.Die(
DamageInteraction(interaction.resolution, SourceEntry(player), interaction.cause, interaction.hitPos) DamageInteraction(interaction.resolution, SourceEntry(player), interaction.cause, interaction.hitPos)

View file

@ -23,7 +23,7 @@ import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource} import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles._
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ShieldCharge, SpawningActivity, VehicleDismountActivity, VehicleMountActivity} import net.psforever.objects.vital.{DamagingActivity, InGameActivity, ReconstructionActivity, ShieldCharge, SpawningActivity, VehicleDismountActivity, VehicleMountActivity}
import net.psforever.objects.vital.environment.EnvironmentReason import net.psforever.objects.vital.environment.EnvironmentReason
import net.psforever.objects.vital.etc.SuicideReason import net.psforever.objects.vital.etc.SuicideReason
import net.psforever.objects.zones._ import net.psforever.objects.zones._
@ -679,14 +679,18 @@ class VehicleControl(vehicle: Vehicle)
*/ */
def doInteractingWithDeath(obj: PlanetSideServerObject, body: PieceOfEnvironment, data: Option[OxygenStateTarget]): Unit = { def doInteractingWithDeath(obj: PlanetSideServerObject, body: PieceOfEnvironment, data: Option[OxygenStateTarget]): Unit = {
if (!obj.Destroyed) { if (!obj.Destroyed) {
PerformDamage( vehicle.History.findLast { entry => entry.isInstanceOf[ReconstructionActivity] } match {
vehicle, case Some(entry) if System.currentTimeMillis() - entry.time > 4000L =>
DamageInteraction( PerformDamage(
VehicleSource(vehicle), vehicle,
SuicideReason(), DamageInteraction(
vehicle.Position VehicleSource(vehicle),
).calculate() SuicideReason(),
) vehicle.Position
).calculate()
)
case _ =>
}
} }
} }
@ -859,10 +863,8 @@ class VehicleControl(vehicle: Vehicle)
override def StartJammeredStatus(target: Any, dur: Int): Unit = { override def StartJammeredStatus(target: Any, dur: Int): Unit = {
super.StartJammeredStatus(target, dur) super.StartJammeredStatus(target, dur)
val subsystems = vehicle.Subsystems() val subsystems = vehicle.Subsystems()
if (!subsystems.exists { _.Jammed }) {
subsystems.foreach { _.jam() } subsystems.foreach { _.jam() }
vehicleSubsystemMessages(subsystems.flatMap { _.changedMessages(vehicle) }) vehicleSubsystemMessages(subsystems.flatMap { _.changedMessages(vehicle) })
}
} }
override def CancelJammeredStatus(target: Any): Unit = { override def CancelJammeredStatus(target: Any): Unit = {

View file

@ -365,6 +365,8 @@ class PersistenceMonitor(
def PerformLogout(): Unit = { def PerformLogout(): Unit = {
(inZone.Players.find(p => p.name == name), inZone.LivePlayers.find(p => p.Name == name)) match { (inZone.Players.find(p => p.name == name), inZone.LivePlayers.find(p => p.Name == name)) match {
case (Some(avatar), Some(player)) if player.VehicleSeated.nonEmpty => case (Some(avatar), Some(player)) if player.VehicleSeated.nonEmpty =>
//in case the player is holding the llu and disconnects
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DropSpecialItem())
//alive or dead in a vehicle //alive or dead in a vehicle
//if the avatar is dead while in a vehicle, they haven't released yet //if the avatar is dead while in a vehicle, they haven't released yet
AvatarActor.saveAvatarData(avatar) AvatarActor.saveAvatarData(avatar)
@ -381,6 +383,8 @@ class PersistenceMonitor(
PlayerAvatarLogout(avatar, player) PlayerAvatarLogout(avatar, player)
case (Some(avatar), Some(player)) => case (Some(avatar), Some(player)) =>
//in case the player is holding the llu and disconnects
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DropSpecialItem())
//alive or dead, as standard Infantry //alive or dead, as standard Infantry
AvatarActor.saveAvatarData(avatar) AvatarActor.saveAvatarData(avatar)
AvatarActor.finalSavePlayerData(player) AvatarActor.finalSavePlayerData(player)

View file

@ -113,6 +113,7 @@ class CaptureFlagManager(zone: Zone) extends Actor {
case CaptureFlagManager.DropFlag(flag: CaptureFlag) => case CaptureFlagManager.DropFlag(flag: CaptureFlag) =>
flag.Carrier match { flag.Carrier match {
case Some(player: Player) => case Some(player: Player) =>
val newFlag = flag
// Set the flag position to where the player is that dropped it // Set the flag position to where the player is that dropped it
flag.Position = player.Position flag.Position = player.Position
// Remove attached player from flag // Remove attached player from flag
@ -121,6 +122,27 @@ class CaptureFlagManager(zone: Zone) extends Actor {
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.SendPacket(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0))) flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.SendPacket(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0)))
// Send dropped chat message // Send dropped chat message
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagDropped(player.Name, player.Faction, flag.Owner.asInstanceOf[Building].Name), fanfare = false) ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagDropped(player.Name, player.Faction, flag.Owner.asInstanceOf[Building].Name), fanfare = false)
HandleFlagDespawn(flag)
// Register LLU object create task and callback to create on clients
val replacementLlu = CaptureFlag.Constructor(
newFlag.Position,
newFlag.Orientation,
newFlag.Target,
newFlag.Owner,
player.Faction
)
// Add the flag as an amenity and track it internally
val socket = newFlag.Owner.asInstanceOf[Building].GetFlagSocket.get
socket.captureFlag = replacementLlu
TrackFlag(replacementLlu)
TaskWorkflow.execute(WorldSession.CallBackForTask(
GUIDTask.registerObject(socket.Zone.GUID, replacementLlu),
socket.Zone.LocalEvents,
LocalServiceMessage(
socket.Zone.id,
LocalAction.LluSpawned(Service.defaultPlayerGUID, replacementLlu)
)
))
case _ => case _ =>
log.warn("Tried to drop flag but flag has no carrier") log.warn("Tried to drop flag but flag has no carrier")
} }