mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-04-29 15:55:23 +00:00
Yet Another Squad Update (2)
Yet Another Squad Update (2) - not ready to go live
This commit is contained in:
commit
749a611b87
24 changed files with 3409 additions and 2249 deletions
|
|
@ -538,6 +538,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
||||||
continent.actor ! ZoneActor.RewardThisDeath(player)
|
continent.actor ! ZoneActor.RewardThisDeath(player)
|
||||||
|
|
||||||
//player state changes
|
//player state changes
|
||||||
|
sessionLogic.zoning.spawn.avatarActive = false
|
||||||
AvatarActor.updateToolDischargeFor(avatar)
|
AvatarActor.updateToolDischargeFor(avatar)
|
||||||
player.FreeHand.Equipment.foreach { item =>
|
player.FreeHand.Equipment.foreach { item =>
|
||||||
DropEquipmentFromInventory(player)(item)
|
DropEquipmentFromInventory(player)(item)
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
||||||
case "grenade" => ops.customCommandGrenade(session, log)
|
case "grenade" => ops.customCommandGrenade(session, log)
|
||||||
case "macro" => ops.customCommandMacro(session, params)
|
case "macro" => ops.customCommandMacro(session, params)
|
||||||
case "progress" => ops.customCommandProgress(session, params)
|
case "progress" => ops.customCommandProgress(session, params)
|
||||||
|
case "squad" => ops.customCommandSquad(params)
|
||||||
case _ =>
|
case _ =>
|
||||||
// command was not handled
|
// command was not handled
|
||||||
sendResponse(
|
sendResponse(
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
||||||
_,
|
_,
|
||||||
_
|
_
|
||||||
)= pkt
|
)= pkt
|
||||||
|
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
|
||||||
sessionLogic.persist()
|
sessionLogic.persist()
|
||||||
sessionLogic.turnCounterFunc(avatarGuid)
|
sessionLogic.turnCounterFunc(avatarGuid)
|
||||||
sessionLogic.updateBlockMap(player, pos)
|
sessionLogic.updateBlockMap(player, pos)
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,24 @@
|
||||||
package net.psforever.actors.session.normal
|
package net.psforever.actors.session.normal
|
||||||
|
|
||||||
import akka.actor.{ActorContext, ActorRef, typed}
|
import akka.actor.{ActorContext, ActorRef, typed}
|
||||||
import net.psforever.actors.session.support.SessionSquadHandlers.SquadUIElement
|
|
||||||
import net.psforever.actors.session.AvatarActor
|
import net.psforever.actors.session.AvatarActor
|
||||||
import net.psforever.actors.session.support.{SessionData, SessionSquadHandlers, SquadHandlerFunctions}
|
import net.psforever.actors.session.support.SessionSquadHandlers.{rethrowSquadServiceResponse, SquadUIElement}
|
||||||
|
import net.psforever.actors.session.support.{SessionData, SessionSquadHandlers, SpawnOperations, SquadHandlerFunctions}
|
||||||
import net.psforever.objects.{Default, LivePlayerList}
|
import net.psforever.objects.{Default, LivePlayerList}
|
||||||
import net.psforever.objects.avatar.Avatar
|
import net.psforever.objects.avatar.Avatar
|
||||||
import net.psforever.packet.game.{CharacterKnowledgeInfo, CharacterKnowledgeMessage, ChatMsg, MemberEvent, PlanetsideAttributeMessage, ReplicationStreamMessage, SquadAction, SquadDefinitionActionMessage, SquadDetailDefinitionUpdateMessage, SquadListing, SquadMemberEvent, SquadMembershipRequest, SquadMembershipResponse, SquadState, SquadStateInfo, SquadWaypointEvent, SquadWaypointRequest, WaypointEvent, WaypointEventAction}
|
import net.psforever.packet.game.{CharacterKnowledgeInfo, CharacterKnowledgeMessage, ChatMsg, MemberEvent, PlanetsideAttributeMessage, ReplicationStreamMessage, SquadAction, SquadDefinitionActionMessage, SquadDetailDefinitionUpdateMessage, SquadListing, SquadMemberEvent, SquadMembershipRequest, SquadMembershipResponse, SquadState, SquadStateInfo, SquadWaypointEvent, SquadWaypointRequest, WaypointEvent, WaypointEventAction}
|
||||||
import net.psforever.services.chat.SquadChannel
|
import net.psforever.services.chat.SquadChannel
|
||||||
import net.psforever.services.teamwork.{SquadResponse, SquadServiceMessage, SquadAction => SquadServiceAction}
|
import net.psforever.services.teamwork.{SquadResponse, SquadServiceMessage, SquadServiceResponse, SquadAction => SquadServiceAction}
|
||||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadListDecoration, SquadResponseType, WaypointSubtype}
|
import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadListDecoration, SquadResponseType, WaypointSubtype}
|
||||||
|
|
||||||
object SquadHandlerLogic {
|
object SquadHandlerLogic {
|
||||||
def apply(ops: SessionSquadHandlers): SquadHandlerLogic = {
|
def apply(ops: SessionSquadHandlers): SquadHandlerLogic = {
|
||||||
new SquadHandlerLogic(ops, ops.context)
|
new SquadHandlerLogic(ops, ops.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def rethrowSquadServiceResponse(response: SquadResponse.Response)(sessionLogic: SessionData): Unit = {
|
||||||
|
sessionLogic.context.self ! SquadServiceResponse("", response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: ActorContext) extends SquadHandlerFunctions {
|
class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: ActorContext) extends SquadHandlerFunctions {
|
||||||
|
|
@ -28,17 +32,17 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
||||||
/* packet */
|
/* packet */
|
||||||
|
|
||||||
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit = {
|
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit = {
|
||||||
/*val SquadDefinitionActionMessage(u1, u2, action) = pkt
|
val SquadDefinitionActionMessage(u1, u2, action) = pkt
|
||||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Definition(u1, u2, action))*/
|
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Definition(u1, u2, action))
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleSquadMemberRequest(pkt: SquadMembershipRequest): Unit = {
|
def handleSquadMemberRequest(pkt: SquadMembershipRequest): Unit = {
|
||||||
/*val SquadMembershipRequest(request_type, char_id, unk3, player_name, unk5) = pkt
|
val SquadMembershipRequest(request_type, char_id, unk3, player_name, unk5) = pkt
|
||||||
squadService ! SquadServiceMessage(
|
squadService ! SquadServiceMessage(
|
||||||
player,
|
player,
|
||||||
continent,
|
continent,
|
||||||
SquadServiceAction.Membership(request_type, char_id, unk3, player_name, unk5)
|
SquadServiceAction.Membership(request_type, char_id, unk3, player_name, unk5)
|
||||||
)*/
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleSquadWaypointRequest(pkt: SquadWaypointRequest): Unit = {
|
def handleSquadWaypointRequest(pkt: SquadWaypointRequest): Unit = {
|
||||||
|
|
@ -59,6 +63,7 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
||||||
def handle(response: SquadResponse.Response, excluded: Iterable[Long]): Unit = {
|
def handle(response: SquadResponse.Response, excluded: Iterable[Long]): Unit = {
|
||||||
if (!excluded.exists(_ == avatar.id)) {
|
if (!excluded.exists(_ == avatar.id)) {
|
||||||
response match {
|
response match {
|
||||||
|
/* these messages will never be queued for later */
|
||||||
case SquadResponse.ListSquadFavorite(line, task) =>
|
case SquadResponse.ListSquadFavorite(line, task) =>
|
||||||
sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), line, SquadAction.ListSquadFavorite(task)))
|
sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), line, SquadAction.ListSquadFavorite(task)))
|
||||||
|
|
||||||
|
|
@ -106,12 +111,69 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
||||||
case SquadResponse.Detail(guid, detail) =>
|
case SquadResponse.Detail(guid, detail) =>
|
||||||
sendResponse(SquadDetailDefinitionUpdateMessage(guid, detail))
|
sendResponse(SquadDetailDefinitionUpdateMessage(guid, detail))
|
||||||
|
|
||||||
case SquadResponse.IdentifyAsSquadLeader(squad_guid) =>
|
|
||||||
sendResponse(SquadDefinitionActionMessage(squad_guid, 0, SquadAction.IdentifyAsSquadLeader()))
|
|
||||||
|
|
||||||
case SquadResponse.SetListSquad(squad_guid) =>
|
case SquadResponse.SetListSquad(squad_guid) =>
|
||||||
sendResponse(SquadDefinitionActionMessage(squad_guid, 0, SquadAction.SetListSquad()))
|
sendResponse(SquadDefinitionActionMessage(squad_guid, 0, SquadAction.SetListSquad()))
|
||||||
|
|
||||||
|
case SquadResponse.SquadSearchResults(results) =>
|
||||||
|
//TODO positive squad search results message?
|
||||||
|
if(results.nonEmpty) {
|
||||||
|
results.foreach { guid =>
|
||||||
|
sendResponse(SquadDefinitionActionMessage(
|
||||||
|
guid,
|
||||||
|
0,
|
||||||
|
SquadAction.SquadListDecorator(SquadListDecoration.SearchResult))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendResponse(SquadDefinitionActionMessage(player.GUID, 0, SquadAction.NoSquadSearchResults()))
|
||||||
|
}
|
||||||
|
sendResponse(SquadDefinitionActionMessage(player.GUID, 0, SquadAction.CancelSquadSearch()))
|
||||||
|
|
||||||
|
case SquadResponse.UpdateMembers(_, positions) =>
|
||||||
|
val pairedEntries = positions.collect {
|
||||||
|
case entry if ops.squadUI.contains(entry.char_id) =>
|
||||||
|
(entry, ops.squadUI(entry.char_id))
|
||||||
|
}
|
||||||
|
//prune entries
|
||||||
|
val updatedEntries = pairedEntries
|
||||||
|
.collect({
|
||||||
|
case (entry, element) if entry.zone_number != element.zone =>
|
||||||
|
//zone gets updated for these entries
|
||||||
|
sendResponse(
|
||||||
|
SquadMemberEvent.UpdateZone(ops.squad_supplement_id, entry.char_id, element.index, entry.zone_number)
|
||||||
|
)
|
||||||
|
ops.squadUI(entry.char_id) =
|
||||||
|
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
||||||
|
entry
|
||||||
|
case (entry, element)
|
||||||
|
if entry.health != element.health || entry.armor != element.armor || entry.pos != element.position =>
|
||||||
|
//other elements that need to be updated
|
||||||
|
ops.squadUI(entry.char_id) =
|
||||||
|
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
||||||
|
entry
|
||||||
|
})
|
||||||
|
.filterNot(_.char_id == avatar.id) //we want to update our backend, but not our frontend
|
||||||
|
if (updatedEntries.nonEmpty) {
|
||||||
|
sendResponse(
|
||||||
|
SquadState(
|
||||||
|
PlanetSideGUID(ops.squad_supplement_id),
|
||||||
|
updatedEntries.map { entry =>
|
||||||
|
SquadStateInfo(entry.char_id, entry.health, entry.armor, entry.pos)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* queue below messages for later if the initial conditions are inappropriate */
|
||||||
|
case msg if !sessionLogic.zoning.spawn.startEnqueueSquadMessages =>
|
||||||
|
sessionLogic.zoning.spawn.enqueueNewActivity(
|
||||||
|
SpawnOperations.ActivityQueuedTask(rethrowSquadServiceResponse(msg), 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* these messages will be queued for later if initial conditions are inappropriate */
|
||||||
|
case SquadResponse.IdentifyAsSquadLeader(squad_guid) =>
|
||||||
|
sendResponse(SquadDefinitionActionMessage(squad_guid, 0, SquadAction.IdentifyAsSquadLeader()))
|
||||||
|
|
||||||
case SquadResponse.Membership(request_type, unk1, unk2, charId, opt_char_id, player_name, unk5, unk6) =>
|
case SquadResponse.Membership(request_type, unk1, unk2, charId, opt_char_id, player_name, unk5, unk6) =>
|
||||||
val name = request_type match {
|
val name = request_type match {
|
||||||
case SquadResponseType.Invite if unk5 =>
|
case SquadResponseType.Invite if unk5 =>
|
||||||
|
|
@ -270,59 +332,9 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
||||||
//the players have already been swapped in the backend object
|
//the players have already been swapped in the backend object
|
||||||
ops.PromoteSquadUIElements(squad, from_index)
|
ops.PromoteSquadUIElements(squad, from_index)
|
||||||
|
|
||||||
case SquadResponse.UpdateMembers(_, positions) =>
|
|
||||||
val pairedEntries = positions.collect {
|
|
||||||
case entry if ops.squadUI.contains(entry.char_id) =>
|
|
||||||
(entry, ops.squadUI(entry.char_id))
|
|
||||||
}
|
|
||||||
//prune entries
|
|
||||||
val updatedEntries = pairedEntries
|
|
||||||
.collect({
|
|
||||||
case (entry, element) if entry.zone_number != element.zone =>
|
|
||||||
//zone gets updated for these entries
|
|
||||||
sendResponse(
|
|
||||||
SquadMemberEvent.UpdateZone(ops.squad_supplement_id, entry.char_id, element.index, entry.zone_number)
|
|
||||||
)
|
|
||||||
ops.squadUI(entry.char_id) =
|
|
||||||
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
|
||||||
entry
|
|
||||||
case (entry, element)
|
|
||||||
if entry.health != element.health || entry.armor != element.armor || entry.pos != element.position =>
|
|
||||||
//other elements that need to be updated
|
|
||||||
ops.squadUI(entry.char_id) =
|
|
||||||
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
|
||||||
entry
|
|
||||||
})
|
|
||||||
.filterNot(_.char_id == avatar.id) //we want to update our backend, but not our frontend
|
|
||||||
if (updatedEntries.nonEmpty) {
|
|
||||||
sendResponse(
|
|
||||||
SquadState(
|
|
||||||
PlanetSideGUID(ops.squad_supplement_id),
|
|
||||||
updatedEntries.map { entry =>
|
|
||||||
SquadStateInfo(entry.char_id, entry.health, entry.armor, entry.pos)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case SquadResponse.CharacterKnowledge(charId, name, certs, u1, u2, zone) =>
|
case SquadResponse.CharacterKnowledge(charId, name, certs, u1, u2, zone) =>
|
||||||
sendResponse(CharacterKnowledgeMessage(charId, Some(CharacterKnowledgeInfo(name, certs, u1, u2, zone))))
|
sendResponse(CharacterKnowledgeMessage(charId, Some(CharacterKnowledgeInfo(name, certs, u1, u2, zone))))
|
||||||
|
|
||||||
case SquadResponse.SquadSearchResults(_/*results*/) =>
|
|
||||||
//TODO positive squad search results message?
|
|
||||||
// if(results.nonEmpty) {
|
|
||||||
// results.foreach { guid =>
|
|
||||||
// sendResponse(SquadDefinitionActionMessage(
|
|
||||||
// guid,
|
|
||||||
// 0,
|
|
||||||
// SquadAction.SquadListDecorator(SquadListDecoration.SearchResult))
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// sendResponse(SquadDefinitionActionMessage(player.GUID, 0, SquadAction.NoSquadSearchResults()))
|
|
||||||
// }
|
|
||||||
// sendResponse(SquadDefinitionActionMessage(player.GUID, 0, SquadAction.CancelSquadSearch()))
|
|
||||||
|
|
||||||
case SquadResponse.InitWaypoints(char_id, waypoints) =>
|
case SquadResponse.InitWaypoints(char_id, waypoints) =>
|
||||||
waypoints.foreach {
|
waypoints.foreach {
|
||||||
case (waypoint_type, info, unk) =>
|
case (waypoint_type, info, unk) =>
|
||||||
|
|
@ -349,6 +361,9 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
||||||
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
|
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
|
||||||
sendResponse(SquadWaypointEvent.Remove(ops.squad_supplement_id, char_id, waypoint_type))
|
sendResponse(SquadWaypointEvent.Remove(ops.squad_supplement_id, char_id, waypoint_type))
|
||||||
|
|
||||||
|
case SquadResponse.SquadRelatedComment(comment, messageType) =>
|
||||||
|
sendResponse(ChatMsg(messageType, comment))
|
||||||
|
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
||||||
ops.GetVehicleAndSeat() match {
|
ops.GetVehicleAndSeat() match {
|
||||||
case (Some(obj), Some(0)) =>
|
case (Some(obj), Some(0)) =>
|
||||||
//we're driving the vehicle
|
//we're driving the vehicle
|
||||||
|
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
|
||||||
sessionLogic.persist()
|
sessionLogic.persist()
|
||||||
sessionLogic.turnCounterFunc(player.GUID)
|
sessionLogic.turnCounterFunc(player.GUID)
|
||||||
sessionLogic.general.fallHeightTracker(pos.z)
|
sessionLogic.general.fallHeightTracker(pos.z)
|
||||||
|
|
@ -128,6 +129,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
||||||
ops.GetVehicleAndSeat() match {
|
ops.GetVehicleAndSeat() match {
|
||||||
case (Some(obj), Some(0)) =>
|
case (Some(obj), Some(0)) =>
|
||||||
//we're driving the vehicle
|
//we're driving the vehicle
|
||||||
|
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
|
||||||
sessionLogic.persist()
|
sessionLogic.persist()
|
||||||
sessionLogic.turnCounterFunc(player.GUID)
|
sessionLogic.turnCounterFunc(player.GUID)
|
||||||
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
|
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
|
||||||
|
|
@ -217,6 +219,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
|
||||||
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) =>
|
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) =>
|
||||||
()
|
()
|
||||||
case _ =>
|
case _ =>
|
||||||
|
sessionLogic.zoning.spawn.tryQueuedActivity() //todo conditionals?
|
||||||
sessionLogic.persist()
|
sessionLogic.persist()
|
||||||
sessionLogic.turnCounterFunc(player.GUID)
|
sessionLogic.turnCounterFunc(player.GUID)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -450,6 +450,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
|
||||||
sessionLogic.zoning.CancelZoningProcess()
|
sessionLogic.zoning.CancelZoningProcess()
|
||||||
|
|
||||||
//player state changes
|
//player state changes
|
||||||
|
sessionLogic.zoning.spawn.avatarActive = false
|
||||||
AvatarActor.updateToolDischargeFor(avatar)
|
AvatarActor.updateToolDischargeFor(avatar)
|
||||||
player.FreeHand.Equipment.foreach { item =>
|
player.FreeHand.Equipment.foreach { item =>
|
||||||
DropEquipmentFromInventory(player)(item)
|
DropEquipmentFromInventory(player)(item)
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
||||||
_,
|
_,
|
||||||
_
|
_
|
||||||
)= pkt
|
)= pkt
|
||||||
|
sessionLogic.zoning.spawn.tryQueuedActivity(vel)
|
||||||
sessionLogic.persist()
|
sessionLogic.persist()
|
||||||
sessionLogic.turnCounterFunc(avatarGuid)
|
sessionLogic.turnCounterFunc(avatarGuid)
|
||||||
sessionLogic.updateBlockMap(player, pos)
|
sessionLogic.updateBlockMap(player, pos)
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
package net.psforever.actors.session.spectator
|
package net.psforever.actors.session.spectator
|
||||||
|
|
||||||
import akka.actor.{ActorContext, typed}
|
import akka.actor.{ActorContext, typed}
|
||||||
import net.psforever.actors.session.support.SessionSquadHandlers.SquadUIElement
|
|
||||||
import net.psforever.actors.session.AvatarActor
|
import net.psforever.actors.session.AvatarActor
|
||||||
import net.psforever.actors.session.support.{SessionData, SessionSquadHandlers, SquadHandlerFunctions}
|
import net.psforever.actors.session.support.SessionSquadHandlers.{rethrowSquadServiceResponse, SquadUIElement}
|
||||||
|
import net.psforever.actors.session.support.{SessionData, SessionSquadHandlers, SpawnOperations, SquadHandlerFunctions}
|
||||||
import net.psforever.objects.{Default, LivePlayerList}
|
import net.psforever.objects.{Default, LivePlayerList}
|
||||||
import net.psforever.objects.avatar.Avatar
|
import net.psforever.objects.avatar.Avatar
|
||||||
import net.psforever.packet.game.{CharacterKnowledgeInfo, CharacterKnowledgeMessage, PlanetsideAttributeMessage, ReplicationStreamMessage, SquadAction, SquadDefinitionActionMessage, SquadDetailDefinitionUpdateMessage, SquadListing, SquadMemberEvent, SquadMembershipRequest, SquadMembershipResponse, SquadState, SquadStateInfo, SquadWaypointEvent, SquadWaypointRequest, WaypointEventAction}
|
import net.psforever.packet.game.{CharacterKnowledgeInfo, CharacterKnowledgeMessage, ChatMsg, PlanetsideAttributeMessage, ReplicationStreamMessage, SquadAction, SquadDefinitionActionMessage, SquadDetailDefinitionUpdateMessage, SquadListing, SquadMemberEvent, SquadMembershipRequest, SquadMembershipResponse, SquadState, SquadStateInfo, SquadWaypointEvent, SquadWaypointRequest, WaypointEventAction}
|
||||||
import net.psforever.services.chat.SquadChannel
|
import net.psforever.services.chat.SquadChannel
|
||||||
import net.psforever.services.teamwork.SquadResponse
|
import net.psforever.services.teamwork.SquadResponse
|
||||||
import net.psforever.types.{PlanetSideGUID, SquadListDecoration, SquadResponseType}
|
import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadListDecoration, SquadResponseType}
|
||||||
|
|
||||||
object SquadHandlerLogic {
|
object SquadHandlerLogic {
|
||||||
def apply(ops: SessionSquadHandlers): SquadHandlerLogic = {
|
def apply(ops: SessionSquadHandlers): SquadHandlerLogic = {
|
||||||
|
|
@ -77,6 +77,62 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
||||||
}
|
}
|
||||||
sendResponse(SquadDefinitionActionMessage(guid, 0, SquadAction.SquadListDecorator(decoration)))
|
sendResponse(SquadDefinitionActionMessage(guid, 0, SquadAction.SquadListDecorator(decoration)))
|
||||||
|
|
||||||
|
case SquadResponse.SquadSearchResults(results) =>
|
||||||
|
//TODO positive squad search results message?
|
||||||
|
if(results.nonEmpty) {
|
||||||
|
results.foreach { guid =>
|
||||||
|
sendResponse(SquadDefinitionActionMessage(
|
||||||
|
guid,
|
||||||
|
0,
|
||||||
|
SquadAction.SquadListDecorator(SquadListDecoration.SearchResult))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendResponse(SquadDefinitionActionMessage(player.GUID, 0, SquadAction.NoSquadSearchResults()))
|
||||||
|
}
|
||||||
|
sendResponse(SquadDefinitionActionMessage(player.GUID, 0, SquadAction.CancelSquadSearch()))
|
||||||
|
|
||||||
|
case SquadResponse.UpdateMembers(_, positions) =>
|
||||||
|
val pairedEntries = positions.collect {
|
||||||
|
case entry if ops.squadUI.contains(entry.char_id) =>
|
||||||
|
(entry, ops.squadUI(entry.char_id))
|
||||||
|
}
|
||||||
|
//prune entries
|
||||||
|
val updatedEntries = pairedEntries
|
||||||
|
.collect({
|
||||||
|
case (entry, element) if entry.zone_number != element.zone =>
|
||||||
|
//zone gets updated for these entries
|
||||||
|
sendResponse(
|
||||||
|
SquadMemberEvent.UpdateZone(ops.squad_supplement_id, entry.char_id, element.index, entry.zone_number)
|
||||||
|
)
|
||||||
|
ops.squadUI(entry.char_id) =
|
||||||
|
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
||||||
|
entry
|
||||||
|
case (entry, element)
|
||||||
|
if entry.health != element.health || entry.armor != element.armor || entry.pos != element.position =>
|
||||||
|
//other elements that need to be updated
|
||||||
|
ops.squadUI(entry.char_id) =
|
||||||
|
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
||||||
|
entry
|
||||||
|
})
|
||||||
|
.filterNot(_.char_id == avatar.id) //we want to update our backend, but not our frontend
|
||||||
|
if (updatedEntries.nonEmpty) {
|
||||||
|
sendResponse(
|
||||||
|
SquadState(
|
||||||
|
PlanetSideGUID(ops.squad_supplement_id),
|
||||||
|
updatedEntries.map { entry =>
|
||||||
|
SquadStateInfo(entry.char_id, entry.health, entry.armor, entry.pos)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* queue below messages for later if the initial conditions are inappropriate */
|
||||||
|
case msg if !sessionLogic.zoning.spawn.startEnqueueSquadMessages =>
|
||||||
|
sessionLogic.zoning.spawn.enqueueNewActivity(
|
||||||
|
SpawnOperations.ActivityQueuedTask(rethrowSquadServiceResponse(msg), 1)
|
||||||
|
)
|
||||||
|
|
||||||
case SquadResponse.Detail(guid, detail) =>
|
case SquadResponse.Detail(guid, detail) =>
|
||||||
sendResponse(SquadDetailDefinitionUpdateMessage(guid, detail))
|
sendResponse(SquadDetailDefinitionUpdateMessage(guid, detail))
|
||||||
|
|
||||||
|
|
@ -132,47 +188,15 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SquadResponse.UpdateMembers(_, positions) =>
|
|
||||||
val pairedEntries = positions.collect {
|
|
||||||
case entry if ops.squadUI.contains(entry.char_id) =>
|
|
||||||
(entry, ops.squadUI(entry.char_id))
|
|
||||||
}
|
|
||||||
//prune entries
|
|
||||||
val updatedEntries = pairedEntries
|
|
||||||
.collect({
|
|
||||||
case (entry, element) if entry.zone_number != element.zone =>
|
|
||||||
//zone gets updated for these entries
|
|
||||||
sendResponse(
|
|
||||||
SquadMemberEvent.UpdateZone(ops.squad_supplement_id, entry.char_id, element.index, entry.zone_number)
|
|
||||||
)
|
|
||||||
ops.squadUI(entry.char_id) =
|
|
||||||
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
|
||||||
entry
|
|
||||||
case (entry, element)
|
|
||||||
if entry.health != element.health || entry.armor != element.armor || entry.pos != element.position =>
|
|
||||||
//other elements that need to be updated
|
|
||||||
ops.squadUI(entry.char_id) =
|
|
||||||
SquadUIElement(element.name, element.outfit, element.index, entry.zone_number, entry.health, entry.armor, entry.pos)
|
|
||||||
entry
|
|
||||||
})
|
|
||||||
.filterNot(_.char_id == avatar.id) //we want to update our backend, but not our frontend
|
|
||||||
if (updatedEntries.nonEmpty) {
|
|
||||||
sendResponse(
|
|
||||||
SquadState(
|
|
||||||
PlanetSideGUID(ops.squad_supplement_id),
|
|
||||||
updatedEntries.map { entry =>
|
|
||||||
SquadStateInfo(entry.char_id, entry.health, entry.armor, entry.pos)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case SquadResponse.CharacterKnowledge(charId, name, certs, u1, u2, zone) =>
|
case SquadResponse.CharacterKnowledge(charId, name, certs, u1, u2, zone) =>
|
||||||
sendResponse(CharacterKnowledgeMessage(charId, Some(CharacterKnowledgeInfo(name, certs, u1, u2, zone))))
|
sendResponse(CharacterKnowledgeMessage(charId, Some(CharacterKnowledgeInfo(name, certs, u1, u2, zone))))
|
||||||
|
|
||||||
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
|
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
|
||||||
sendResponse(SquadWaypointEvent.Remove(ops.squad_supplement_id, char_id, waypoint_type))
|
sendResponse(SquadWaypointEvent.Remove(ops.squad_supplement_id, char_id, waypoint_type))
|
||||||
|
|
||||||
|
case SquadResponse.SquadRelatedComment(comment, messageType) =>
|
||||||
|
sendResponse(ChatMsg(messageType, comment))
|
||||||
|
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,30 @@
|
||||||
package net.psforever.actors.session.support
|
package net.psforever.actors.session.support
|
||||||
|
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
|
import akka.actor.{ActorRef => ClassicActorRef}
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.{ActorContext, typed}
|
import akka.actor.{ActorContext, typed}
|
||||||
|
import akka.pattern.ask
|
||||||
|
import akka.util.Timeout
|
||||||
import net.psforever.actors.session.spectator.SpectatorMode
|
import net.psforever.actors.session.spectator.SpectatorMode
|
||||||
import net.psforever.actors.session.{AvatarActor, SessionActor}
|
import net.psforever.actors.session.{AvatarActor, SessionActor}
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
|
import net.psforever.objects.LivePlayerList
|
||||||
import net.psforever.objects.sourcing.PlayerSource
|
import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.objects.zones.ZoneInfo
|
import net.psforever.objects.zones.ZoneInfo
|
||||||
import net.psforever.packet.game.SetChatFilterMessage
|
import net.psforever.packet.game.SetChatFilterMessage
|
||||||
import net.psforever.services.chat.{DefaultChannel, SquadChannel}
|
import net.psforever.services.chat.{DefaultChannel, SquadChannel}
|
||||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||||
|
import net.psforever.services.teamwork.{SquadResponse, SquadService, SquadServiceResponse}
|
||||||
import net.psforever.types.ChatMessageType.CMT_QUIT
|
import net.psforever.types.ChatMessageType.CMT_QUIT
|
||||||
import org.log4s.Logger
|
import org.log4s.Logger
|
||||||
|
|
||||||
import java.util.concurrent.{Executors, TimeUnit}
|
import java.util.concurrent.{Executors, TimeUnit}
|
||||||
import scala.annotation.unused
|
import scala.annotation.unused
|
||||||
import scala.collection.mutable
|
import scala.collection.{Seq, mutable}
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
import scala.util.Success
|
||||||
//
|
//
|
||||||
import net.psforever.actors.zone.BuildingActor
|
import net.psforever.actors.zone.BuildingActor
|
||||||
import net.psforever.login.WorldSession
|
import net.psforever.login.WorldSession
|
||||||
|
|
@ -54,6 +61,7 @@ class ChatOperations(
|
||||||
val sessionLogic: SessionData,
|
val sessionLogic: SessionData,
|
||||||
val avatarActor: typed.ActorRef[AvatarActor.Command],
|
val avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||||
val chatService: typed.ActorRef[ChatService.Command],
|
val chatService: typed.ActorRef[ChatService.Command],
|
||||||
|
val squadService: ClassicActorRef,
|
||||||
val cluster: typed.ActorRef[InterstellarClusterService.Command],
|
val cluster: typed.ActorRef[InterstellarClusterService.Command],
|
||||||
implicit val context: ActorContext
|
implicit val context: ActorContext
|
||||||
) extends CommonSessionInterfacingFunctionality {
|
) extends CommonSessionInterfacingFunctionality {
|
||||||
|
|
@ -74,6 +82,10 @@ class ChatOperations(
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
private val chatServiceAdapter: ActorRef[ChatService.MessageResponse] = context.self.toTyped[ChatService.MessageResponse]
|
private val chatServiceAdapter: ActorRef[ChatService.MessageResponse] = context.self.toTyped[ChatService.MessageResponse]
|
||||||
|
|
||||||
|
private implicit lazy val timeout: Timeout = Timeout(2.seconds)
|
||||||
|
|
||||||
|
private var invitationList: Array[String] = Array()
|
||||||
|
|
||||||
def JoinChannel(channel: ChatChannel): Unit = {
|
def JoinChannel(channel: ChatChannel): Unit = {
|
||||||
chatService ! ChatService.JoinChannel(chatServiceAdapter, sessionLogic, channel)
|
chatService ! ChatService.JoinChannel(chatServiceAdapter, sessionLogic, channel)
|
||||||
channels ++= List(channel)
|
channels ++= List(channel)
|
||||||
|
|
@ -1284,6 +1296,47 @@ class ChatOperations(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def customCommandSquad(params: Seq[String]): Boolean = {
|
||||||
|
params match {
|
||||||
|
case "invites" :: _ =>
|
||||||
|
invitationList = Array()
|
||||||
|
ask(squadService, SquadService.ListAllCurrentInvites)
|
||||||
|
.onComplete {
|
||||||
|
case Success(msg @ SquadServiceResponse(_, _, SquadResponse.WantsSquadPosition(_, str))) =>
|
||||||
|
invitationList = str.replaceAll("/s","").split(",")
|
||||||
|
context.self.forward(msg)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
case "accept" :: names if names.contains("all") =>
|
||||||
|
squadService ! SquadService.ChainAcceptance(player, player.CharId, Nil)
|
||||||
|
case "accept" :: names if names.nonEmpty =>
|
||||||
|
//when passing indices to existing invite list, the indices are 1-based
|
||||||
|
val results = (names.flatMap(_.toIntOption.flatMap(i => invitationList.lift(i-1))) ++ names)
|
||||||
|
.distinct
|
||||||
|
.flatMap { name =>
|
||||||
|
LivePlayerList.WorldPopulation { case (_, p) => p.name.equalsIgnoreCase(name) }
|
||||||
|
.map(_.id.toLong)
|
||||||
|
}
|
||||||
|
squadService ! SquadService.ChainAcceptance(player, player.CharId, results)
|
||||||
|
|
||||||
|
case "reject" :: names if names.contains("all") =>
|
||||||
|
squadService ! SquadService.ChainRejection(player, player.CharId, Nil)
|
||||||
|
case "reject" :: names if names.nonEmpty =>
|
||||||
|
//when passing indices to existing invite list, the indices are 1-based
|
||||||
|
val results = (names.flatMap(_.toIntOption.flatMap(i => invitationList.lift(i-1))) ++ names)
|
||||||
|
.distinct
|
||||||
|
.flatMap { name =>
|
||||||
|
LivePlayerList.WorldPopulation { case (_, p) => p.name.equalsIgnoreCase(name) }
|
||||||
|
.map(_.id.toLong)
|
||||||
|
}
|
||||||
|
squadService ! SquadService.ChainRejection(player, player.CharId, results)
|
||||||
|
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
def firstParam[T](
|
def firstParam[T](
|
||||||
session: Session,
|
session: Session,
|
||||||
buffer: Iterable[String],
|
buffer: Iterable[String],
|
||||||
|
|
|
||||||
|
|
@ -165,15 +165,15 @@ class SessionData(
|
||||||
case LookupResult("squad", endpoint) =>
|
case LookupResult("squad", endpoint) =>
|
||||||
squadService = endpoint
|
squadService = endpoint
|
||||||
buildDependentOperationsForSquad(endpoint)
|
buildDependentOperationsForSquad(endpoint)
|
||||||
|
buildDependentOperationsForChat(chatService, endpoint, cluster)
|
||||||
true
|
true
|
||||||
case ICS.InterstellarClusterServiceKey.Listing(listings) =>
|
case ICS.InterstellarClusterServiceKey.Listing(listings) =>
|
||||||
cluster = listings.head
|
cluster = listings.head
|
||||||
buildDependentOperationsForZoning(galaxyService, cluster)
|
buildDependentOperationsForZoning(galaxyService, cluster)
|
||||||
buildDependentOperationsForChat(chatService, cluster)
|
|
||||||
true
|
true
|
||||||
case ChatService.ChatServiceKey.Listing(listings) =>
|
case ChatService.ChatServiceKey.Listing(listings) =>
|
||||||
chatService = listings.head
|
chatService = listings.head
|
||||||
buildDependentOperationsForChat(chatService, cluster)
|
buildDependentOperationsForChat(chatService, squadService, cluster)
|
||||||
true
|
true
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -200,9 +200,16 @@ class SessionData(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildDependentOperationsForChat(chatService: typed.ActorRef[ChatService.Command], clusterActor: typed.ActorRef[ICS.Command]): Unit = {
|
def buildDependentOperationsForChat(
|
||||||
if (chatOpt.isEmpty && chatService != Default.typed.Actor && clusterActor != Default.typed.Actor) {
|
chatService: typed.ActorRef[ChatService.Command],
|
||||||
chatOpt = Some(new ChatOperations(sessionLogic=this, avatarActor, chatService, clusterActor, context))
|
squadService: ActorRef,
|
||||||
|
clusterActor: typed.ActorRef[ICS.Command]
|
||||||
|
): Unit = {
|
||||||
|
if (chatOpt.isEmpty &&
|
||||||
|
chatService != Default.typed.Actor &&
|
||||||
|
squadService !=Default.Actor &&
|
||||||
|
clusterActor != Default.typed.Actor) {
|
||||||
|
chatOpt = Some(new ChatOperations(sessionLogic=this, avatarActor, chatService, squadService, clusterActor, context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -547,6 +554,7 @@ class SessionData(
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
accountPersistence ! AccountPersistenceService.Logout(avatar.name)
|
accountPersistence ! AccountPersistenceService.Logout(avatar.name)
|
||||||
}
|
}
|
||||||
|
squad.cleanUpSquadCards()
|
||||||
middlewareActor ! MiddlewareActor.Teardown()
|
middlewareActor ! MiddlewareActor.Teardown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
package net.psforever.actors.session.support
|
package net.psforever.actors.session.support
|
||||||
|
|
||||||
import akka.actor.{ActorContext, ActorRef, typed}
|
import akka.actor.{ActorContext, ActorRef, typed}
|
||||||
|
import net.psforever.services.teamwork.SquadServiceResponse
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
//
|
//
|
||||||
import net.psforever.actors.session.AvatarActor
|
import net.psforever.actors.session.AvatarActor
|
||||||
|
|
@ -36,6 +38,9 @@ object SessionSquadHandlers {
|
||||||
armor: Int,
|
armor: Int,
|
||||||
position: Vector3
|
position: Vector3
|
||||||
)
|
)
|
||||||
|
def rethrowSquadServiceResponse(response: SquadResponse.Response)(sessionLogic: SessionData): Unit = {
|
||||||
|
sessionLogic.context.self ! SquadServiceResponse("", response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SessionSquadHandlers(
|
class SessionSquadHandlers(
|
||||||
|
|
@ -84,6 +89,7 @@ class SessionSquadHandlers(
|
||||||
sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), 0, SquadAction.Unknown(18)))
|
sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), 0, SquadAction.Unknown(18)))
|
||||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.InitSquadList())
|
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.InitSquadList())
|
||||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.InitCharId())
|
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.InitCharId())
|
||||||
|
cleanUpSquadCards()
|
||||||
squadSetup = RespawnSquadSetup
|
squadSetup = RespawnSquadSetup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,4 +351,12 @@ class SessionSquadHandlers(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def cleanUpSquadCards(): Unit = {
|
||||||
|
squadUI.foreach { case (id, card) =>
|
||||||
|
sendResponse(SquadMemberEvent.Remove(squad_supplement_id, id, card.index))
|
||||||
|
}
|
||||||
|
squadUI.clear()
|
||||||
|
squad_supplement_id = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,12 @@ import akka.actor.typed.scaladsl.adapter._
|
||||||
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
|
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
import net.psforever.actors.session.support.SpawnOperations.ActivityQueuedTask
|
||||||
import net.psforever.login.WorldSession
|
import net.psforever.login.WorldSession
|
||||||
import net.psforever.objects.avatar.{BattleRank, DeployableToolbox}
|
import net.psforever.objects.avatar.{BattleRank, DeployableToolbox}
|
||||||
import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics}
|
import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics}
|
||||||
import net.psforever.objects.definition.converter.OCM
|
import net.psforever.objects.definition.converter.OCM
|
||||||
|
import net.psforever.objects.entity.WorldEntity
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
import net.psforever.objects.inventory.InventoryItem
|
||||||
import net.psforever.objects.serverobject.interior.Sidedness
|
import net.psforever.objects.serverobject.interior.Sidedness
|
||||||
import net.psforever.objects.serverobject.mount.Seat
|
import net.psforever.objects.serverobject.mount.Seat
|
||||||
|
|
@ -22,7 +24,6 @@ import net.psforever.packet.game.GenericAction.FirstPersonViewWithEffect
|
||||||
import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, CloudInfo, GenericActionMessage, GenericObjectActionEnum, HackState7, MailMessage, ObjectDetectedMessage, SessionStatistic, StormInfo, TriggeredSound, WeatherMessage}
|
import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, CloudInfo, GenericActionMessage, GenericObjectActionEnum, HackState7, MailMessage, ObjectDetectedMessage, SessionStatistic, StormInfo, TriggeredSound, WeatherMessage}
|
||||||
import net.psforever.services.chat.DefaultChannel
|
import net.psforever.services.chat.DefaultChannel
|
||||||
|
|
||||||
import scala.collection.mutable
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
|
@ -84,8 +85,8 @@ object ZoningOperations {
|
||||||
|
|
||||||
private final val zoningCountdownMessages: Seq[Int] = Seq(5, 10, 20)
|
private final val zoningCountdownMessages: Seq[Int] = Seq(5, 10, 20)
|
||||||
|
|
||||||
def reportProgressionSystem(sessionActor: ActorRef): Unit = {
|
def reportProgressionSystem(logic: SessionData): Unit = {
|
||||||
sessionActor ! SessionActor.SendResponse(
|
logic.context.self ! SessionActor.SendResponse(
|
||||||
MailMessage(
|
MailMessage(
|
||||||
"High Command",
|
"High Command",
|
||||||
"Progress versus Promotion",
|
"Progress versus Promotion",
|
||||||
|
|
@ -186,6 +187,14 @@ object ZoningOperations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object SpawnOperations {
|
||||||
|
final case class ActivityQueuedTask(task: SessionData => Unit, delayBeforeNext: Int, repeat: Int = 0)
|
||||||
|
|
||||||
|
def sendEventMessage(msg: ChatMsg)(sessionLogic: SessionData): Unit = {
|
||||||
|
sessionLogic.sendResponse(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ZoningOperations(
|
class ZoningOperations(
|
||||||
val sessionLogic: SessionData,
|
val sessionLogic: SessionData,
|
||||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||||
|
|
@ -672,11 +681,15 @@ class ZoningOperations(
|
||||||
zoningType = Zoning.Method.Login
|
zoningType = Zoning.Method.Login
|
||||||
response match {
|
response match {
|
||||||
case Some((zone, spawnPoint)) =>
|
case Some((zone, spawnPoint)) =>
|
||||||
spawn.loginChatMessage.addOne("@login_reposition_to_friendly_facility") //Your previous location was held by the enemy. You have been moved to the nearest friendly facility.
|
spawn.enqueueNewActivity(ActivityQueuedTask(
|
||||||
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@login_reposition_to_friendly_facility")), 20)
|
||||||
|
)
|
||||||
val (pos, ori) = spawnPoint.SpecificPoint(player)
|
val (pos, ori) = spawnPoint.SpecificPoint(player)
|
||||||
spawn.LoadZonePhysicalSpawnPoint(zone.id, pos, ori, respawnTime = 0 seconds, Some(spawnPoint))
|
spawn.LoadZonePhysicalSpawnPoint(zone.id, pos, ori, respawnTime = 0 seconds, Some(spawnPoint))
|
||||||
case _ =>
|
case _ =>
|
||||||
spawn.loginChatMessage.addOne("@login_reposition_to_sanctuary") //Your previous location was held by the enemy. As there were no operational friendly facilities on that continent, you have been brought back to your Sanctuary.
|
spawn.enqueueNewActivity(ActivityQueuedTask(
|
||||||
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@login_reposition_to_sanctuary")), 20)
|
||||||
|
)
|
||||||
RequestSanctuaryZoneSpawn(player, player.Zone.Number)
|
RequestSanctuaryZoneSpawn(player, player.Zone.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1865,7 +1878,6 @@ class ZoningOperations(
|
||||||
|
|
||||||
class SpawnOperations() {
|
class SpawnOperations() {
|
||||||
private[session] var deadState: DeadState.Value = DeadState.Dead
|
private[session] var deadState: DeadState.Value = DeadState.Dead
|
||||||
private[session] var loginChatMessage: mutable.ListBuffer[String] = new mutable.ListBuffer[String]()
|
|
||||||
private[session] var amsSpawnPoints: List[SpawnPoint] = Nil
|
private[session] var amsSpawnPoints: List[SpawnPoint] = Nil
|
||||||
private[session] var noSpawnPointHere: Boolean = false
|
private[session] var noSpawnPointHere: Boolean = false
|
||||||
private[session] var setupAvatarFunc: () => Unit = AvatarCreate
|
private[session] var setupAvatarFunc: () => Unit = AvatarCreate
|
||||||
|
|
@ -1888,9 +1900,14 @@ class ZoningOperations(
|
||||||
private[session] var drawDeloyableIcon: PlanetSideGameObject with Deployable => Unit = RedrawDeployableIcons
|
private[session] var drawDeloyableIcon: PlanetSideGameObject with Deployable => Unit = RedrawDeployableIcons
|
||||||
private[session] var populateAvatarAwardRibbonsFunc: (Int, Long) => Unit = setupAvatarAwardMessageDelivery
|
private[session] var populateAvatarAwardRibbonsFunc: (Int, Long) => Unit = setupAvatarAwardMessageDelivery
|
||||||
private[session] var setAvatar: Boolean = false
|
private[session] var setAvatar: Boolean = false
|
||||||
|
private[session] var avatarActive: Boolean = false
|
||||||
private[session] var reviveTimer: Cancellable = Default.Cancellable
|
private[session] var reviveTimer: Cancellable = Default.Cancellable
|
||||||
private[session] var respawnTimer: Cancellable = Default.Cancellable
|
private[session] var respawnTimer: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
|
private var queuedActivities: Seq[SpawnOperations.ActivityQueuedTask] = Seq()
|
||||||
|
private val initialActivityDelay: Int = 4
|
||||||
|
private var nextActivityDelay: Int = 0
|
||||||
|
|
||||||
private var statisticsPacketFunc: () => Unit = loginAvatarStatisticsFields
|
private var statisticsPacketFunc: () => Unit = loginAvatarStatisticsFields
|
||||||
|
|
||||||
/* packets */
|
/* packets */
|
||||||
|
|
@ -1899,6 +1916,7 @@ class ZoningOperations(
|
||||||
val ReleaseAvatarRequestMessage() = pkt
|
val ReleaseAvatarRequestMessage() = pkt
|
||||||
log.info(s"${player.Name} on ${continent.id} has released")
|
log.info(s"${player.Name} on ${continent.id} has released")
|
||||||
reviveTimer.cancel()
|
reviveTimer.cancel()
|
||||||
|
avatarActive = false
|
||||||
GoToDeploymentMap()
|
GoToDeploymentMap()
|
||||||
HandleReleaseAvatar(player, continent)
|
HandleReleaseAvatar(player, continent)
|
||||||
}
|
}
|
||||||
|
|
@ -1986,16 +2004,19 @@ class ZoningOperations(
|
||||||
}
|
}
|
||||||
val noFriendlyPlayersInZone = friendlyPlayersInZone == 0
|
val noFriendlyPlayersInZone = friendlyPlayersInZone == 0
|
||||||
if (inZone.map.cavern) {
|
if (inZone.map.cavern) {
|
||||||
loginChatMessage.addOne("@reset_sanctuary_locked")
|
enqueueNewActivity(ActivityQueuedTask(
|
||||||
//You have been returned to the sanctuary because the location you logged out is not available.
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_locked")), 20)
|
||||||
|
) //You have been returned to the sanctuary because the location you logged out is not available.
|
||||||
player.Zone = Zone.Nowhere
|
player.Zone = Zone.Nowhere
|
||||||
} else if (ourBuildings.isEmpty && (amsSpawnPoints.isEmpty || noFriendlyPlayersInZone)) {
|
} else if (ourBuildings.isEmpty && (amsSpawnPoints.isEmpty || noFriendlyPlayersInZone)) {
|
||||||
loginChatMessage.addOne("@reset_sanctuary_locked")
|
enqueueNewActivity(ActivityQueuedTask(
|
||||||
//You have been returned to the sanctuary because the location you logged out is not available.
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_locked")), 20)
|
||||||
|
) //You have been returned to the sanctuary because the location you logged out is not available.
|
||||||
player.Zone = Zone.Nowhere
|
player.Zone = Zone.Nowhere
|
||||||
} else if (friendlyPlayersInZone > 137 || playersInZone.size > 413) {
|
} else if (friendlyPlayersInZone > 137 || playersInZone.size > 413) {
|
||||||
loginChatMessage.addOne("@reset_sanctuary_full")
|
enqueueNewActivity(ActivityQueuedTask(
|
||||||
//You have been returned to the sanctuary because the zone you logged out on is full.
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_full")), 20)
|
||||||
|
) //You have been returned to the sanctuary because the zone you logged out on is full.
|
||||||
player.Zone = Zone.Nowhere
|
player.Zone = Zone.Nowhere
|
||||||
} else {
|
} else {
|
||||||
val inBuildingSOI = buildings.filter { b =>
|
val inBuildingSOI = buildings.filter { b =>
|
||||||
|
|
@ -2012,8 +2033,9 @@ class ZoningOperations(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (noFriendlyPlayersInZone) {
|
if (noFriendlyPlayersInZone) {
|
||||||
loginChatMessage.addOne("@reset_sanctuary_inactive")
|
enqueueNewActivity(ActivityQueuedTask(
|
||||||
//You have been returned to the sanctuary because the location you logged out is not available.
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_inactive")), 20)
|
||||||
|
) //You have been returned to the sanctuary because the location you logged out is not available.
|
||||||
player.Zone = Zone.Nowhere
|
player.Zone = Zone.Nowhere
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2021,8 +2043,9 @@ class ZoningOperations(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//player is dead; go back to sanctuary
|
//player is dead; go back to sanctuary
|
||||||
loginChatMessage.addOne("@reset_sanctuary_inactive")
|
enqueueNewActivity(ActivityQueuedTask(
|
||||||
//You have been returned to the sanctuary because the location you logged out is not available.
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_inactive")), 20)
|
||||||
|
) //You have been returned to the sanctuary because the location you logged out is not available.
|
||||||
player.Zone = Zone.Nowhere
|
player.Zone = Zone.Nowhere
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2120,11 +2143,15 @@ class ZoningOperations(
|
||||||
zoningType = Zoning.Method.Login
|
zoningType = Zoning.Method.Login
|
||||||
response match {
|
response match {
|
||||||
case Some((zone, spawnPoint)) =>
|
case Some((zone, spawnPoint)) =>
|
||||||
loginChatMessage.addOne("@login_reposition_to_friendly_facility") //Your previous location was held by the enemy. You have been moved to the nearest friendly facility.
|
enqueueNewActivity(ActivityQueuedTask(
|
||||||
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@login_reposition_to_friendly_facility")), 20)
|
||||||
|
) //Your previous location was held by the enemy. You have been moved to the nearest friendly facility.
|
||||||
val (pos, ori) = spawnPoint.SpecificPoint(player)
|
val (pos, ori) = spawnPoint.SpecificPoint(player)
|
||||||
LoadZonePhysicalSpawnPoint(zone.id, pos, ori, respawnTime = 0 seconds, Some(spawnPoint))
|
LoadZonePhysicalSpawnPoint(zone.id, pos, ori, respawnTime = 0 seconds, Some(spawnPoint))
|
||||||
case _ =>
|
case _ =>
|
||||||
loginChatMessage.addOne("@login_reposition_to_sanctuary") //Your previous location was held by the enemy. As there were no operational friendly facilities on that continent, you have been brought back to your Sanctuary.
|
enqueueNewActivity(ActivityQueuedTask(
|
||||||
|
SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@login_reposition_to_sanctuary")), 20)
|
||||||
|
) //Your previous location was held by the enemy. As there were no operational friendly facilities on that continent, you have been brought back to your Sanctuary.
|
||||||
RequestSanctuaryZoneSpawn(player, player.Zone.Number)
|
RequestSanctuaryZoneSpawn(player, player.Zone.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2926,6 +2953,7 @@ class ZoningOperations(
|
||||||
respawnTimer.cancel()
|
respawnTimer.cancel()
|
||||||
reviveTimer.cancel()
|
reviveTimer.cancel()
|
||||||
deadState = DeadState.RespawnTime
|
deadState = DeadState.RespawnTime
|
||||||
|
avatarActive = false
|
||||||
sendResponse(
|
sendResponse(
|
||||||
AvatarDeadStateMessage(
|
AvatarDeadStateMessage(
|
||||||
DeadState.RespawnTime,
|
DeadState.RespawnTime,
|
||||||
|
|
@ -3299,7 +3327,7 @@ class ZoningOperations(
|
||||||
tavatar.scorecard.CurrentLife.prior.isEmpty && /* no revives */
|
tavatar.scorecard.CurrentLife.prior.isEmpty && /* no revives */
|
||||||
tplayer.History.size == 1 /* did nothing but come into existence */
|
tplayer.History.size == 1 /* did nothing but come into existence */
|
||||||
) {
|
) {
|
||||||
ZoningOperations.reportProgressionSystem(context.self)
|
enqueueNewActivity(ActivityQueuedTask(ZoningOperations.reportProgressionSystem, 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
||||||
|
|
@ -3561,8 +3589,6 @@ class ZoningOperations(
|
||||||
*/
|
*/
|
||||||
def TurnCounterLogin(guid: PlanetSideGUID): Unit = {
|
def TurnCounterLogin(guid: PlanetSideGUID): Unit = {
|
||||||
NormalTurnCounter(guid)
|
NormalTurnCounter(guid)
|
||||||
loginChatMessage.foreach { msg => sendResponse(ChatMsg(zoningChatMessageType, wideContents=false, "", msg, None)) }
|
|
||||||
loginChatMessage.clear()
|
|
||||||
CancelZoningProcess()
|
CancelZoningProcess()
|
||||||
sessionLogic.turnCounterFunc = NormalTurnCounter
|
sessionLogic.turnCounterFunc = NormalTurnCounter
|
||||||
}
|
}
|
||||||
|
|
@ -3773,6 +3799,7 @@ class ZoningOperations(
|
||||||
nextSpawnPoint = Some(obj) //set fallback
|
nextSpawnPoint = Some(obj) //set fallback
|
||||||
zoningStatus = Zoning.Status.Deconstructing
|
zoningStatus = Zoning.Status.Deconstructing
|
||||||
player.allowInteraction = false
|
player.allowInteraction = false
|
||||||
|
avatarActive = false
|
||||||
if (player.death_by == 0) {
|
if (player.death_by == 0) {
|
||||||
player.death_by = 1
|
player.death_by = 1
|
||||||
}
|
}
|
||||||
|
|
@ -3791,6 +3818,7 @@ class ZoningOperations(
|
||||||
zoningStatus = Zoning.Status.None
|
zoningStatus = Zoning.Status.None
|
||||||
player.death_by = math.min(player.death_by, 0)
|
player.death_by = math.min(player.death_by, 0)
|
||||||
player.allowInteraction = true
|
player.allowInteraction = true
|
||||||
|
avatarActive = true
|
||||||
nextSpawnPoint.foreach { tube =>
|
nextSpawnPoint.foreach { tube =>
|
||||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, tube.Position, tube.Orientation.z)))
|
sendResponse(PlayerStateShiftMessage(ShiftState(0, tube.Position, tube.Orientation.z)))
|
||||||
nextSpawnPoint = None
|
nextSpawnPoint = None
|
||||||
|
|
@ -3815,7 +3843,7 @@ class ZoningOperations(
|
||||||
|
|
||||||
private def usingSpawnTubeAnimation(): Unit = {
|
private def usingSpawnTubeAnimation(): Unit = {
|
||||||
getSpawnTubeOwner
|
getSpawnTubeOwner
|
||||||
.collect { case (sp, owner @ Some(_)) => (sp, owner) }
|
.collect { case (sp, owner@Some(_)) => (sp, owner) }
|
||||||
.collect {
|
.collect {
|
||||||
case (sp, Some(_: Vehicle)) =>
|
case (sp, Some(_: Vehicle)) =>
|
||||||
ZoningOperations.usingVehicleSpawnTubeAnimation(sp.Zone, sp.WhichSide, sp.Faction, player.Position, sp.Orientation, List(player.Name))
|
ZoningOperations.usingVehicleSpawnTubeAnimation(sp.Zone, sp.WhichSide, sp.Faction, player.Position, sp.Orientation, List(player.Name))
|
||||||
|
|
@ -3823,6 +3851,84 @@ class ZoningOperations(
|
||||||
ZoningOperations.usingFacilitySpawnTubeAnimation(sp.Zone, sp.WhichSide, sp.Faction, player.Position, sp.Orientation, List(player.Name))
|
ZoningOperations.usingFacilitySpawnTubeAnimation(sp.Zone, sp.WhichSide, sp.Faction, player.Position, sp.Orientation, List(player.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def startEnqueueSquadMessages: Boolean = {
|
||||||
|
sessionLogic.zoning.zoneReload && sessionLogic.zoning.spawn.setAvatar && player.isAlive
|
||||||
|
}
|
||||||
|
|
||||||
|
def enqueueNewActivity(newTasking: SpawnOperations.ActivityQueuedTask): Unit = {
|
||||||
|
if (avatarActive && queuedActivities.isEmpty) {
|
||||||
|
nextActivityDelay = initialActivityDelay
|
||||||
|
}
|
||||||
|
queuedActivities = queuedActivities :+ newTasking
|
||||||
|
}
|
||||||
|
|
||||||
|
def tryQueuedActivity(pos1: Vector3, pos2: Vector3, distanceSquared: Float = 1f) : Unit = {
|
||||||
|
if (!avatarActive) {
|
||||||
|
if (Vector3.DistanceSquared(pos1, pos2) > distanceSquared) {
|
||||||
|
startExecutingQueuedActivity()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
countDownUntilQueuedActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def tryQueuedActivity(vel: Option[Vector3]) : Unit = {
|
||||||
|
if (!avatarActive) {
|
||||||
|
if (WorldEntity.isMoving(vel)) {
|
||||||
|
startExecutingQueuedActivity()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
countDownUntilQueuedActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def tryQueuedActivity() : Unit = {
|
||||||
|
if (!avatarActive) {
|
||||||
|
startExecutingQueuedActivity()
|
||||||
|
} else {
|
||||||
|
countDownUntilQueuedActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def addDelayBeforeNextQueuedActivity(delay: Int): Unit = {
|
||||||
|
if (nextActivityDelay == 0) {
|
||||||
|
nextActivityDelay = initialActivityDelay
|
||||||
|
} else if (queuedActivities.nonEmpty) {
|
||||||
|
val lastActivity = queuedActivities.last
|
||||||
|
if (lastActivity.delayBeforeNext < delay) {
|
||||||
|
queuedActivities = queuedActivities.dropRight(1) :+ lastActivity.copy(delayBeforeNext = delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def startExecutingQueuedActivity(): Unit = {
|
||||||
|
avatarActive = startEnqueueSquadMessages
|
||||||
|
if (nextActivityDelay == 0) {
|
||||||
|
nextActivityDelay = initialActivityDelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def countDownUntilQueuedActivity(): Unit = {
|
||||||
|
if (nextActivityDelay > 0) {
|
||||||
|
nextActivityDelay -= 1
|
||||||
|
} else if (queuedActivities.nonEmpty) {
|
||||||
|
val task :: rest = queuedActivities
|
||||||
|
queuedActivities = if (task.repeat > 0) {
|
||||||
|
task.copy(repeat = task.repeat - 1) +: rest //positive: repeat immediately
|
||||||
|
} else if (task.repeat < 0) {
|
||||||
|
rest :+ task.copy(repeat = task.repeat + 1) //negative: repeat after all other tasks have been completed
|
||||||
|
} else {
|
||||||
|
rest
|
||||||
|
}
|
||||||
|
nextActivityDelay = task.delayBeforeNext
|
||||||
|
task.task(sessionLogic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def clearAllQueuedActivity(): Unit = {
|
||||||
|
queuedActivities = Seq()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def doorsThatShouldBeClosedOrBeOpenedByRange(
|
def doorsThatShouldBeClosedOrBeOpenedByRange(
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@ import akka.actor.ActorRef
|
||||||
import net.psforever.objects.avatar.Certification
|
import net.psforever.objects.avatar.Certification
|
||||||
import net.psforever.objects.teamwork.Squad
|
import net.psforever.objects.teamwork.Squad
|
||||||
import net.psforever.packet.game.{SquadDetail, SquadInfo, WaypointEventAction, WaypointInfo}
|
import net.psforever.packet.game.{SquadDetail, SquadInfo, WaypointEventAction, WaypointInfo}
|
||||||
import net.psforever.types.{PlanetSideGUID, SquadResponseType, SquadWaypoint}
|
import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadResponseType, SquadWaypoint}
|
||||||
import net.psforever.services.GenericEventBusMsg
|
import net.psforever.services.GenericEventBusMsg
|
||||||
|
|
||||||
final case class SquadServiceResponse(channel: String, exclude: Iterable[Long], response: SquadResponse.Response)
|
final case class SquadServiceResponse(channel: String, exclude: Iterable[Long], response: SquadResponse.Response)
|
||||||
|
|
@ -41,6 +41,16 @@ object SquadResponse {
|
||||||
unk5: Boolean,
|
unk5: Boolean,
|
||||||
unk6: Option[Option[String]]
|
unk6: Option[Option[String]]
|
||||||
) extends Response //see SquadMembershipResponse
|
) extends Response //see SquadMembershipResponse
|
||||||
|
object Membership {
|
||||||
|
def apply(
|
||||||
|
requestType: SquadResponseType.Value,
|
||||||
|
unk3: Long,
|
||||||
|
unk4: Option[Long],
|
||||||
|
playerName: String,
|
||||||
|
unk5: Boolean
|
||||||
|
): Membership = new Membership(requestType, unk1 = 0, unk2 = 0, unk3, unk4, playerName, unk5, Some(None))
|
||||||
|
}
|
||||||
|
|
||||||
final case class WantsSquadPosition(leader_char_id: Long, bid_name: String) extends Response
|
final case class WantsSquadPosition(leader_char_id: Long, bid_name: String) extends Response
|
||||||
final case class Join(squad: Squad, positionsToUpdate: List[Int], channel: String, ref: ActorRef) extends Response
|
final case class Join(squad: Squad, positionsToUpdate: List[Int], channel: String, ref: ActorRef) extends Response
|
||||||
final case class Leave(squad: Squad, positionsToUpdate: List[(Long, Int)]) extends Response
|
final case class Leave(squad: Squad, positionsToUpdate: List[(Long, Int)]) extends Response
|
||||||
|
|
@ -73,4 +83,6 @@ object SquadResponse {
|
||||||
unk2: Int,
|
unk2: Int,
|
||||||
zoneNumber: Int
|
zoneNumber: Int
|
||||||
) extends Response
|
) extends Response
|
||||||
|
|
||||||
|
final case class SquadRelatedComment(str: String, messageType: ChatMessageType = ChatMessageType.UNK_227) extends Response
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class SquadSubscriptionEntity {
|
||||||
case str if str.matches("\\d+") =>
|
case str if str.matches("\\d+") =>
|
||||||
Publish(to.toLong, msg, excluded)
|
Publish(to.toLong, msg, excluded)
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"Publish(String): subscriber information is an unhandled format - $to")
|
log.warn(s"Publish(String): subscriber information is an unhandled format - $to; message $msg dropped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +148,7 @@ class SquadSubscriptionEntity {
|
||||||
case Some(user) =>
|
case Some(user) =>
|
||||||
user ! SquadServiceResponse("", msg)
|
user ! SquadServiceResponse("", msg)
|
||||||
case None =>
|
case None =>
|
||||||
log.warn(s"Publish(Long): subscriber information can not be found - $to")
|
log.warn(s"Publish(Long): subscriber information can not be found - $to; message $msg dropped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +174,7 @@ class SquadSubscriptionEntity {
|
||||||
* @param msg a message that can be stored in a `SquadServiceResponse` object
|
* @param msg a message that can be stored in a `SquadServiceResponse` object
|
||||||
*/
|
*/
|
||||||
def Publish[ANY >: Any](to: ANY, msg: SquadResponse.Response): Unit = {
|
def Publish[ANY >: Any](to: ANY, msg: SquadResponse.Response): Unit = {
|
||||||
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to")
|
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to; message $msg dropped")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -187,7 +187,7 @@ class SquadSubscriptionEntity {
|
||||||
* @param excluded a group of character identifier numbers who should not receive the message
|
* @param excluded a group of character identifier numbers who should not receive the message
|
||||||
*/
|
*/
|
||||||
def Publish[ANY >: Any](to: ANY, msg: SquadResponse.Response, excluded: Iterable[Long]): Unit = {
|
def Publish[ANY >: Any](to: ANY, msg: SquadResponse.Response, excluded: Iterable[Long]): Unit = {
|
||||||
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to")
|
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to; message $msg dropped")
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The following functions are related to common communications of squad information, mainly detail. */
|
/* The following functions are related to common communications of squad information, mainly detail. */
|
||||||
|
|
@ -211,7 +211,7 @@ class SquadSubscriptionEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch an intial message entailing the strategic information and the composition of this squad.
|
* Dispatch an initial message entailing the strategic information and the composition of this squad.
|
||||||
* The details of the squad will be updated in full and be sent to all indicated observers.
|
* The details of the squad will be updated in full and be sent to all indicated observers.
|
||||||
* @see `SquadService.PublishFullDetails`
|
* @see `SquadService.PublishFullDetails`
|
||||||
* @param guid the unique squad identifier to be used when composing the details for this message
|
* @param guid the unique squad identifier to be used when composing the details for this message
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.teamwork.SquadFeatures
|
||||||
|
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
|
||||||
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
|
import scala.annotation.unused
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilized to redirect an (accepted) invitation request to the proper squad leader.
|
||||||
|
* An anticipated result of clarifying permission to request invitation
|
||||||
|
* to a squad belonging to some player who is not the squad leader.
|
||||||
|
* No direct action causes this message.
|
||||||
|
* This invitation is handled by the squad leader.
|
||||||
|
* @param originalRequester player who would be joining the squad;
|
||||||
|
* also the player who invited the player who will become the squad leader
|
||||||
|
* @param features squad
|
||||||
|
*/
|
||||||
|
final case class IndirectInvite(originalRequester: Player, features: SquadFeatures)
|
||||||
|
extends Invitation(originalRequester.CharId, originalRequester.Name) {
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit = {
|
||||||
|
indirectInviteFunc(this, originalRequester, invitedPlayer, invitingPlayer, otherName)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
@unused player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
@unused invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit = {
|
||||||
|
//tplayer / invitedPlayer is actually the squad leader
|
||||||
|
if (SquadInvitationManager.canEnrollInSquad(features, originalRequester.CharId)) {
|
||||||
|
val leaderCharId = player.CharId
|
||||||
|
val invitedPlayer = originalRequester.CharId
|
||||||
|
manager
|
||||||
|
.handleVacancyInvite(features, invitedPlayer, invitedPlayer, originalRequester)
|
||||||
|
.collect {
|
||||||
|
case (_, position) if manager.joinSquad(originalRequester, features, position) =>
|
||||||
|
manager.acceptanceMessages(invitedPlayer, invitedPlayer, originalRequester.Name)
|
||||||
|
//clean up invitations specifically for this squad and this position
|
||||||
|
val cleanedUpActiveInvitesForSquadAndPosition = manager.cleanUpActiveInvitesForSquadAndPosition(features.Squad.GUID, position)
|
||||||
|
cleanedUpActiveInvitesForSquadAndPosition.collect { case (id, _) =>
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val cleanedUpQueuedInvites = manager.cleanUpQueuedInvitesForSquadAndPosition(features.Squad.GUID, position)
|
||||||
|
if (features.Squad.Capacity == features.Squad.Size) {
|
||||||
|
val cleanedUpActiveInvites = manager.cleanUpActiveInvitesForSquad(features.Squad.GUID)
|
||||||
|
cleanedUpActiveInvites.collect { case (id, invites) =>
|
||||||
|
invites.foreach(_.handleCancel(manager, player, id))
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(manager.cleanUpQueuedInvitesForSquad(features.Squad.GUID) ++ cleanedUpActiveInvites ++ cleanedUpQueuedInvites).collectFirst { case _ =>
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (cleanedUpQueuedInvites.nonEmpty) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
features
|
||||||
|
}
|
||||||
|
.orElse {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your invitation to ${player.Name} was accepted, but failed.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You have failed to joined the squad '${features.Squad.Task}'.")
|
||||||
|
)
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
@unused squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit = {
|
||||||
|
doRejection(manager, player, rejectingPlayer)
|
||||||
|
manager.publish(
|
||||||
|
originalRequester.CharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your request to join the squad has been refused.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
rejectingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You refused ${originalRequester.Name}'s request to join this squad.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
features.DeniedPlayers(originalRequester.CharId)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val invitingPlayer = originalRequester.CharId
|
||||||
|
val invitingPlayerName = originalRequester.Name
|
||||||
|
val actingPlayer = player.CharId
|
||||||
|
val leaderCharId = features.Squad.Leader.CharId
|
||||||
|
val leaderName = features.Squad.Leader.Name
|
||||||
|
if (actingPlayer == handlingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
invitingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You were declined admission to a squad.")
|
||||||
|
)
|
||||||
|
} else if (actingPlayer == invitingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"$invitingPlayerName has rescinded the offer to join the squad.")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The request from $invitingPlayerName to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer to $leaderName to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean = true
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures] = Some(features)
|
||||||
|
|
||||||
|
def getPlayer: Player = originalRequester
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == originalRequester.CharId
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean = features.Squad.GUID == guid
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean = false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.teamwork.SquadFeatures
|
||||||
|
import net.psforever.services.teamwork.SquadInvitationManager
|
||||||
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base of all objects that exist for the purpose of communicating invitation from one player to the next.
|
||||||
|
* @param charId inviting player's unique identifier number
|
||||||
|
* @param name inviting player's name
|
||||||
|
*/
|
||||||
|
abstract class Invitation(charId: Long, name: String) {
|
||||||
|
def inviterCharId: Long = charId
|
||||||
|
def inviterName: String = name
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A branched response for processing (new) invitation objects that have been submitted to the system.<br>
|
||||||
|
* <br>
|
||||||
|
* A comparison is performed between the original invitation object and an invitation object
|
||||||
|
* that represents the potential modification or redirection of the current active invitation obect.
|
||||||
|
* Any further action is only performed when an "is equal" comparison is `true`.
|
||||||
|
* When passing, the system publishes up to two messages
|
||||||
|
* to users that would anticipate being informed of squad join activity.
|
||||||
|
* @param indirectInviteFunc the method that cans the responding behavior should an `IndirectInvite` object being consumed
|
||||||
|
* @param invitedPlayer the unique character identifier for the player being invited;
|
||||||
|
* in actuality, represents the player who will address the invitation object
|
||||||
|
* @param invitingPlayer the unique character identifier for the player who invited the former
|
||||||
|
* @param otherName a name to be used in message composition
|
||||||
|
*/
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
*
|
||||||
|
* @param manager subscription package
|
||||||
|
* @param handlingPlayer player who was intended to handle this invitation
|
||||||
|
* @param player player who caused cleanup action
|
||||||
|
*/
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @return active player entity associated with this invite;
|
||||||
|
* can be `null` as some invitations do not retain such character data
|
||||||
|
*/
|
||||||
|
def getPlayer: Player
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.teamwork.SquadFeatures
|
||||||
|
import net.psforever.services.teamwork.SquadInvitationManager.FinishStartSquad
|
||||||
|
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
|
||||||
|
import net.psforever.types.{PlanetSideGUID, SquadResponseType}
|
||||||
|
|
||||||
|
import scala.annotation.unused
|
||||||
|
import scala.util.Success
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilized when one player issues an invite for some other player for a squad that does not yet exist.
|
||||||
|
* This invitation is handled by the player who would be joining the squad.
|
||||||
|
*
|
||||||
|
* @param futureSquadLeader player who wishes to become the leader of a squad
|
||||||
|
*/
|
||||||
|
final case class InvitationToCreateASquad(futureSquadLeader: Player)
|
||||||
|
extends Invitation(futureSquadLeader.CharId, futureSquadLeader.Name) {
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit = {
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Invite, inviterCharId, Some(invitedPlayer), futureSquadLeader.Name, unk5 = false)
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
inviterCharId,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Invite, invitedPlayer, Some(inviterCharId), futureSquadLeader.Name, unk5 = true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit = {
|
||||||
|
if (manager.notLimitedByEnrollmentInSquad(invitedPlayerSquadOpt, invitedPlayer)) {
|
||||||
|
//accepted an invitation to join an existing squad
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
val leaderCharId = futureSquadLeader.CharId
|
||||||
|
manager
|
||||||
|
.askToCreateANewSquad(futureSquadLeader)
|
||||||
|
.onComplete {
|
||||||
|
case Success(FinishStartSquad(features)) =>
|
||||||
|
manager
|
||||||
|
.handleVacancyInvite(features, invitedPlayer, leaderCharId, player)
|
||||||
|
.collect {
|
||||||
|
case (_, line) if manager.joinSquad(player, features, line) =>
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Accept, invitedPlayer, Some(leaderCharId), "", unk5 = false)
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Accept, leaderCharId, Some(invitedPlayer), player.Name, unk5 = true)
|
||||||
|
)
|
||||||
|
//all invitations involving the invited person must be cancelled due to the nature of this acceptance
|
||||||
|
manager.cleanUpQueuedInvitesForPlayer(invitedPlayer)
|
||||||
|
val cleanedUpActiveInvites = manager.cleanUpAllInvitesForPlayer(invitedPlayer)
|
||||||
|
cleanedUpActiveInvites.collect { case (id, invites) =>
|
||||||
|
invites.foreach(_.handleCancel(manager, player, id))
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation involving ${futureSquadLeader.Name} has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
features
|
||||||
|
}
|
||||||
|
.orElse {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Though a squad has been created, a member could not join it.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You could not join ${futureSquadLeader.Name} squad.")
|
||||||
|
)
|
||||||
|
None
|
||||||
|
}
|
||||||
|
//since a squad was created, currently operated by the leader, all invitations related to the leader have changed
|
||||||
|
manager.cleanUpAllInvitesForPlayer(leaderCharId).collectFirst { _ =>
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
org.log4s.getLogger("InvitationToCreateASquad").error("could not create a squad when requested")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
@unused squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit = {
|
||||||
|
//rejectingPlayer is the would-be squad member; the would-be squad leader sent the request and was rejected
|
||||||
|
val invitingPlayerCharId = futureSquadLeader.CharId
|
||||||
|
doRejection(manager, player, rejectingPlayer)
|
||||||
|
manager.publish(
|
||||||
|
rejectingPlayer,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Reject, rejectingPlayer, Some(invitingPlayerCharId), "", unk5 = true)
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitingPlayerCharId,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Reject, invitingPlayerCharId, Some(rejectingPlayer), player.Name, unk5 = false)
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
rejectingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your request to form a squad has been refused.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
manager.refused(rejectingPlayer, futureSquadLeader.CharId)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val actingPlayer = player.CharId
|
||||||
|
val leaderCharId = futureSquadLeader.CharId
|
||||||
|
if (actingPlayer == handlingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"${player.Name} has declined joining into a squad with you, or the offer is no longer valid.")
|
||||||
|
)
|
||||||
|
} else if (actingPlayer == leaderCharId) {
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"${futureSquadLeader.Name} has decided not to join into a squad with you, or the offer is no longer valid.")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer to ${player.Name} to join into a squad with you is no longer valid.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer from ${futureSquadLeader.Name} join into a squad with you is no longer valid.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean = false
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures] = None
|
||||||
|
|
||||||
|
def getPlayer: Player = futureSquadLeader
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == futureSquadLeader.CharId
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean = false
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean = false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.teamwork.SquadFeatures
|
||||||
|
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
|
||||||
|
import net.psforever.types.{PlanetSideGUID, SquadResponseType}
|
||||||
|
|
||||||
|
import scala.annotation.unused
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilized when one squad member issues an invite for some other player.
|
||||||
|
* Accessed by an existing squad member using the "Invite" menu option on another player.
|
||||||
|
* This invitation is handled by the player who would join the squad.
|
||||||
|
*
|
||||||
|
* @param charId unique character identifier of the player who sent the invite
|
||||||
|
* @param name name the player who sent the invite
|
||||||
|
* @param features the squad
|
||||||
|
*/
|
||||||
|
final case class InvitationToJoinSquad(charId: Long, name: String, features: SquadFeatures)
|
||||||
|
extends Invitation(charId, name) {
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit = {
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Invite, charId, Some(invitedPlayer), name, unk5 = false)
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
charId,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Invite, invitedPlayer, Some(charId), name, unk5 = true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
@unused invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit = {
|
||||||
|
if (
|
||||||
|
manager.notLimitedByEnrollmentInSquad(invitedPlayerSquadOpt, invitedPlayer) &&
|
||||||
|
SquadInvitationManager.canEnrollInSquad(features, invitedPlayer)
|
||||||
|
) {
|
||||||
|
//accepted an invitation to join an existing squad
|
||||||
|
val leaderCharId = charId
|
||||||
|
manager
|
||||||
|
.handleVacancyInvite(features, invitedPlayer, charId, player)
|
||||||
|
.collect {
|
||||||
|
case (_, line) if manager.joinSquad(player, features, line) =>
|
||||||
|
//manager.acceptanceMessages(charId, invitedPlayer, player.Name)
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your invitation to ${player.Name} was accepted.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You have joined the squad '${features.Squad.Task}'.")
|
||||||
|
)
|
||||||
|
//all invitations involving the invited person must be cancelled due to the nature of this acceptance
|
||||||
|
manager.cleanUpQueuedInvitesForPlayer(invitedPlayer).collect { case (id, _) =>
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation involving ${player.Name} has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (features.Squad.Capacity == features.Squad.Size) {
|
||||||
|
val cleanedUpActiveInvites = manager.cleanUpActiveInvitesForSquad(features.Squad.GUID)
|
||||||
|
cleanedUpActiveInvites.collect { case (id, invites) =>
|
||||||
|
invites.foreach(_.handleCancel(manager, player, id))
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(manager.cleanUpQueuedInvitesForSquad(features.Squad.GUID) ++ cleanedUpActiveInvites).collectFirst { case _ =>
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
features
|
||||||
|
}
|
||||||
|
.orElse {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your invitation to ${player.Name} was accepted, but failed.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You have failed to joined the squad '${features.Squad.Task}'.")
|
||||||
|
)
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
@unused squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit = {
|
||||||
|
/*if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/
|
||||||
|
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||||
|
doRejection(manager, player, rejectingPlayer)
|
||||||
|
manager.rejectionMessages(rejectingPlayer, charId, player.Name)
|
||||||
|
manager.publish(
|
||||||
|
rejectingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your request to join squad '${features.Squad.Task}' has been refused.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
manager.refused(rejectingPlayer, charId)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val actingPlayer = player.CharId
|
||||||
|
val leaderCharId = features.Squad.Leader.CharId
|
||||||
|
val leaderName = features.Squad.Leader.Name
|
||||||
|
if (actingPlayer == handlingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"${player.Name} has declined to join the squad.")
|
||||||
|
)
|
||||||
|
} else if (actingPlayer == leaderCharId) {
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"$leaderName has rescinded the offer to join the squad.")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer to ${player.Name} to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer from $leaderName to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean = false
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures] = Some(features)
|
||||||
|
|
||||||
|
def getPlayer: Player = null
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == charId
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean = features.Squad.GUID == guid
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean = false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.teamwork.{Member, SquadFeatures}
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
|
||||||
|
import net.psforever.types.{PlanetSideGUID, SquadResponseType}
|
||||||
|
|
||||||
|
import scala.annotation.unused
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilized in conjunction with an external queuing data structure
|
||||||
|
* to search for and submit requests to other players
|
||||||
|
* for the purposes of fill out an unoccupied squad role.
|
||||||
|
* This invitation is handled by the player who would be joining the squad.
|
||||||
|
*
|
||||||
|
* @param squadLeader squad leader
|
||||||
|
* @param features squad with the role
|
||||||
|
* @param position index of the role
|
||||||
|
*/
|
||||||
|
final case class LookingForSquadRoleInvite(squadLeader: Member, features: SquadFeatures, position: Int)
|
||||||
|
extends Invitation(squadLeader.CharId, squadLeader.Name) {
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit = {
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.Membership(SquadResponseType.Invite, invitedPlayer, Some(squadLeader.CharId), squadLeader.Name, unk5 = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
@unused invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit = {
|
||||||
|
val leaderCharId = squadLeader.CharId
|
||||||
|
if (
|
||||||
|
manager.notLimitedByEnrollmentInSquad(invitedPlayerSquadOpt, invitedPlayer) &&
|
||||||
|
SquadInvitationManager.canEnrollInSquad(features, invitedPlayer) &&
|
||||||
|
manager.joinSquad(player, features, position)
|
||||||
|
) {
|
||||||
|
//join this squad
|
||||||
|
//manager.acceptanceMessages(invitedPlayer, requestee.CharId, requestee.Name)
|
||||||
|
val msg = SquadResponse.Membership(SquadResponseType.Accept, invitedPlayer, Some(leaderCharId), player.Name, unk5 = false)
|
||||||
|
manager.publish(leaderCharId, msg)
|
||||||
|
manager.publish(invitedPlayer, msg.copy(unk5 = true))
|
||||||
|
// manager.publish(
|
||||||
|
// invitedPlayer,
|
||||||
|
// SquadResponse.SquadRelatedComment(s"You have accepted ${squadLeader.Name}'s request to join a squad.")
|
||||||
|
// )
|
||||||
|
// manager.publish(
|
||||||
|
// leaderCharId,
|
||||||
|
// SquadResponse.SquadRelatedComment(s"${player.Name} has agreed to joined your squad.")
|
||||||
|
// )
|
||||||
|
//clean up invitations specifically for this squad and this position
|
||||||
|
val cleanedUpActiveInvitesForSquadAndPosition = manager.cleanUpActiveInvitesForSquadAndPosition(features.Squad.GUID, position)
|
||||||
|
cleanedUpActiveInvitesForSquadAndPosition.collect { case (id, _) =>
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val cleanedUpQueuedInvites = manager.cleanUpQueuedInvitesForSquadAndPosition(features.Squad.GUID, position)
|
||||||
|
if (features.Squad.Capacity == features.Squad.Size) {
|
||||||
|
val cleanedUpActiveInvites = manager.cleanUpActiveInvitesForSquad(features.Squad.GUID)
|
||||||
|
cleanedUpActiveInvites.collect { case (id, invites) =>
|
||||||
|
invites.foreach(_.handleCancel(manager, player, id))
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(manager.cleanUpQueuedInvitesForSquad(features.Squad.GUID) ++ cleanedUpActiveInvites ++ cleanedUpQueuedInvites).collectFirst { case _ =>
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (cleanedUpQueuedInvites.nonEmpty) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your accepted an invitation to squad '${features.Squad.Task}', but it failed.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An accepted request to join your squad has failed.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
@unused squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit = {
|
||||||
|
val leaderCharId = squadLeader.CharId
|
||||||
|
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||||
|
doRejection(manager, player, rejectingPlayer)
|
||||||
|
manager.rejectionMessages(rejectingPlayer, leaderCharId, player.Name)
|
||||||
|
manager.publish(
|
||||||
|
rejectingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your request to join squad '${features.Squad.Task}' has been refused.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val faction = player.Faction
|
||||||
|
manager.reloadSearchForRoleInvite(
|
||||||
|
player.Zone.Players.filter(_.faction == faction),
|
||||||
|
rejectingPlayer,
|
||||||
|
features,
|
||||||
|
position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val actingPlayer = player.CharId
|
||||||
|
val leaderCharId = features.Squad.Leader.CharId
|
||||||
|
val leaderName = features.Squad.Leader.Name
|
||||||
|
if (actingPlayer == handlingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"${player.Name} has declined to join the squad.")
|
||||||
|
)
|
||||||
|
} else if (actingPlayer == leaderCharId) {
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"$leaderName has rescinded the offer to join the squad.")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer to ${player.Name} to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer from $leaderName to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean = false
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures] = Some(features)
|
||||||
|
|
||||||
|
def getPlayer: Player = null
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == squadLeader.CharId
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean = features.Squad.GUID == guid
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean = appliesToSquad(guid) && position == squadPosition
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.teamwork.SquadFeatures
|
||||||
|
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
|
||||||
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When requesting to that some other player join a newly-formed squad,
|
||||||
|
* but that player is actually the member of a squad already,
|
||||||
|
* this offer is extended to convert the invitation request into a different invitation request.
|
||||||
|
* The "different invitation" will be asking the leader of the other player's squad if our player can join it.
|
||||||
|
* Only technically an "invitation" in that sense, just for the purposes of handling it.
|
||||||
|
* This "invitation" is handled by the player who tried to initiate the original invitation to the other player.
|
||||||
|
* @param initialRequest player who would be joining the squad
|
||||||
|
* @param invitedPlayer player who would be joining the squad (unique character id)
|
||||||
|
* @param invitedPlayerSquad squad
|
||||||
|
*/
|
||||||
|
case class PermissionToReverseInvitationToSquad(initialRequest: Player, invitedPlayer: Long, invitedPlayerSquad: SquadFeatures)
|
||||||
|
extends Invitation(initialRequest.CharId, initialRequest.Name) {
|
||||||
|
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit = {
|
||||||
|
manager.publish(
|
||||||
|
invitingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"\\#6 The player you tried to invite already belongs to a squad.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"\\#6Would you like to try join that squad? (respond with \\#3/accept\\#6 or \\#3/cancel\\#6)")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit = {
|
||||||
|
manager.createIndirectInvite(player, invitedPlayer, invitedPlayerSquad) //should put it at the front of the list
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit = {
|
||||||
|
/* wordless rejection */
|
||||||
|
}
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
/* wordless rejection */
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val actingPlayer = player.CharId
|
||||||
|
if (actingPlayer != handlingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"A question regarding squad invitations no longer matters.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean = false
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures] = Some(invitedPlayerSquad)
|
||||||
|
|
||||||
|
def getPlayer: Player = initialRequest
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean = invitedPlayer == playerCharId
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean = invitedPlayerSquad.Squad.GUID == guid
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean = false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.teamwork.{Member, SquadFeatures}
|
||||||
|
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
|
||||||
|
import net.psforever.types.{PlanetSideGUID, SquadResponseType}
|
||||||
|
|
||||||
|
import scala.annotation.unused
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilized in conjunction with an external queuing data structure
|
||||||
|
* to search for and submit requests to other players
|
||||||
|
* for the purposes of fill out unoccupied squad roles.
|
||||||
|
* This invitation is handled by the player who would be joining the squad.
|
||||||
|
*
|
||||||
|
* @param squadLeader squad leader
|
||||||
|
* @param features squad
|
||||||
|
* @param position index of a role
|
||||||
|
*/
|
||||||
|
final case class ProximityInvite(squadLeader: Member, features: SquadFeatures, position: Int)
|
||||||
|
extends Invitation(squadLeader.CharId, squadLeader.Name) {
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit = {
|
||||||
|
manager.publish(
|
||||||
|
invitedPlayer,
|
||||||
|
SquadResponse.Membership(
|
||||||
|
SquadResponseType.Invite,
|
||||||
|
invitedPlayer,
|
||||||
|
Some(squadLeader.CharId),
|
||||||
|
squadLeader.Name,
|
||||||
|
unk5 = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit = {
|
||||||
|
val leaderCharId = squadLeader.CharId
|
||||||
|
//this cleanup activity always happens
|
||||||
|
features.ProxyInvites = features.ProxyInvites.filterNot { _ == invitedPlayer }
|
||||||
|
if (
|
||||||
|
manager.notLimitedByEnrollmentInSquad(invitedPlayerSquadOpt, invitedPlayer) &&
|
||||||
|
SquadInvitationManager.canEnrollInSquad(features, invitedPlayer) &&
|
||||||
|
manager.joinSquad(player, features, position)
|
||||||
|
) {
|
||||||
|
//join this squad
|
||||||
|
//manager.acceptanceMessages(invitingPlayer, invitedPlayer, player.Name)
|
||||||
|
val msg = SquadResponse.Membership(SquadResponseType.Accept, invitedPlayer, Some(leaderCharId), player.Name, unk5 = false)
|
||||||
|
manager.publish(leaderCharId, msg)
|
||||||
|
manager.publish(invitedPlayer, msg.copy(unk5 = true))
|
||||||
|
//clean up invitations specifically for this squad and this position
|
||||||
|
val cleanedUpQueuedInvites = manager.cleanUpQueuedInvitesForSquadAndPosition(features.Squad.GUID, position)
|
||||||
|
if (features.Squad.Capacity == features.Squad.Size) {
|
||||||
|
val cleanedUpActiveInvites = manager.cleanUpActiveInvitesForSquad(features.Squad.GUID)
|
||||||
|
cleanedUpActiveInvites.collect { case (id, invites) =>
|
||||||
|
invites.foreach(_.handleCancel(manager, player, id))
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(manager.cleanUpQueuedInvitesForSquad(features.Squad.GUID) ++ cleanedUpActiveInvites ++ cleanedUpQueuedInvites).collectFirst { case _ =>
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (cleanedUpQueuedInvites.nonEmpty) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//if able to attempt to accept this proximity invite, recruitment is still ongoing
|
||||||
|
manager.reloadProximityInvite(player.Zone.Players, invitedPlayer, features, position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
@unused squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit = {
|
||||||
|
/*if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/
|
||||||
|
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||||
|
doRejection(manager, player, rejectingPlayer)
|
||||||
|
manager.rejectionMessage(rejectingPlayer)
|
||||||
|
manager.publish(
|
||||||
|
rejectingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your request to join squad '${features.Squad.Task}' has been refused.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
manager.reloadProximityInvite(player.Zone.Players, rejectingPlayer, features, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val actingPlayer = player.CharId
|
||||||
|
val leaderCharId = squadLeader.CharId
|
||||||
|
if (actingPlayer == handlingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You have declined an offer to join a squad.")
|
||||||
|
)
|
||||||
|
} else if (actingPlayer == leaderCharId) {
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer to join a squad has been cancelled.")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
handlingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer to join into a squad is no longer valid.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean = false
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures] = Some(features)
|
||||||
|
|
||||||
|
def getPlayer: Player = null
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == squadLeader.CharId
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean = features.Squad.GUID == guid
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean = appliesToSquad(guid) && position == squadPosition
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright (c) 2024 PSForever
|
||||||
|
package net.psforever.services.teamwork.invitations
|
||||||
|
|
||||||
|
import net.psforever.objects.Player
|
||||||
|
import net.psforever.objects.teamwork.SquadFeatures
|
||||||
|
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
|
||||||
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
|
import scala.annotation.unused
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilized when one player attempts to join an existing squad in a specific role.
|
||||||
|
* Accessed by the joining player from the squad detail window.
|
||||||
|
* This invitation is handled by the squad leader.
|
||||||
|
*
|
||||||
|
* @param requestee player who requested the role
|
||||||
|
* @param features squad with the role
|
||||||
|
* @param position index of the role
|
||||||
|
*/
|
||||||
|
final case class RequestToJoinSquadRole(requestee: Player, features: SquadFeatures, position: Int)
|
||||||
|
extends Invitation(requestee.CharId, requestee.Name) {
|
||||||
|
def handleInvitation(indirectInviteFunc: (IndirectInvite, Player, Long, Long, String) => Boolean)(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
invitingPlayer: Long,
|
||||||
|
otherName: String
|
||||||
|
): Unit = {
|
||||||
|
SquadInvitationManager.handleRequestRole(manager, requestee, bid = this)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleAcceptance(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
@unused player: Player,
|
||||||
|
invitedPlayer: Long,
|
||||||
|
@unused invitedPlayerSquadOpt: Option[SquadFeatures]
|
||||||
|
): Unit = {
|
||||||
|
//player requested to join a squad's specific position
|
||||||
|
//invitedPlayer is actually the squad leader; petitioner is the actual "invitedPlayer"
|
||||||
|
val leaderCharId = player.CharId
|
||||||
|
val requestingPlayer = requestee.CharId
|
||||||
|
if (
|
||||||
|
SquadInvitationManager.canEnrollInSquad(features, requestee.CharId) &&
|
||||||
|
manager.joinSquad(requestee, features, position)
|
||||||
|
) {
|
||||||
|
//manager.acceptanceMessages(invitedPlayer, requestee.CharId, requestee.Name)
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You have accepted ${requestee.Name}'s request to join your squad.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
requestingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You have joined the squad '${features.Squad.Task}'.")
|
||||||
|
)
|
||||||
|
//clean up invitations specifically for this squad and this position
|
||||||
|
val cleanedUpActiveInvitesForSquadAndPosition = manager.cleanUpActiveInvitesForSquadAndPosition(features.Squad.GUID, position)
|
||||||
|
cleanedUpActiveInvitesForSquadAndPosition.collect { case (id, invites) =>
|
||||||
|
invites.foreach(_.handleCancel(manager, player, id))
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val cleanedUpQueuedInvites = manager.cleanUpQueuedInvitesForSquadAndPosition(features.Squad.GUID, position)
|
||||||
|
if (features.Squad.Capacity == features.Squad.Size) {
|
||||||
|
val cleanedUpActiveInvites = manager.cleanUpActiveInvitesForSquad(features.Squad.GUID)
|
||||||
|
cleanedUpActiveInvites.collect { case (id, _) =>
|
||||||
|
manager.publish(
|
||||||
|
id,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An invitation to join a squad has ended.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(manager.cleanUpQueuedInvitesForSquad(features.Squad.GUID) ++ cleanedUpActiveInvites ++ cleanedUpQueuedInvites).collectFirst { case _ =>
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (cleanedUpQueuedInvites.nonEmpty) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You had invitations that were cancelled due to this action.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
requestingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your invitation to squad '${features.Squad.Task}' was accepted, but failed.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"An accepted request to join your squad has failed.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
@unused player: Player,
|
||||||
|
rejectingPlayer: Long,
|
||||||
|
@unused squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||||
|
): Unit = {
|
||||||
|
if (SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, requestee.CharId)) {
|
||||||
|
//rejected is the would-be squad member; rejectingPlayer is the squad leader who rejected the request
|
||||||
|
doRejection(manager, player, rejectingPlayer)
|
||||||
|
manager.rejectionMessage(rejectingPlayer)
|
||||||
|
manager.publish(
|
||||||
|
rejectingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"Your request to join squad '${features.Squad.Task}' has been refused.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def doRejection(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
rejectingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
features.DeniedPlayers(requestee.CharId)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleCancel(
|
||||||
|
manager: SquadInvitationManager,
|
||||||
|
player: Player,
|
||||||
|
handlingPlayer: Long
|
||||||
|
): Unit = {
|
||||||
|
val invitingPlayer = requestee.CharId
|
||||||
|
val invitingPlayerName = requestee.Name
|
||||||
|
val actingPlayer = player.CharId
|
||||||
|
val leaderCharId = features.Squad.Leader.CharId
|
||||||
|
val leaderName = features.Squad.Leader.Name
|
||||||
|
if (actingPlayer == handlingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
invitingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"You were declined admission to a squad.")
|
||||||
|
)
|
||||||
|
} else if (actingPlayer == invitingPlayer) {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"$invitingPlayerName has rescinded the offer to join the squad.")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
manager.publish(
|
||||||
|
leaderCharId,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The request from $invitingPlayerName to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
manager.publish(
|
||||||
|
invitingPlayer,
|
||||||
|
SquadResponse.SquadRelatedComment(s"The offer to $leaderName to join the squad is no longer valid.")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeAutoApproved: Boolean = true
|
||||||
|
|
||||||
|
def getOptionalSquad: Option[SquadFeatures] = Some(features)
|
||||||
|
|
||||||
|
def getPlayer: Player = requestee
|
||||||
|
|
||||||
|
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == requestee.CharId
|
||||||
|
|
||||||
|
def appliesToSquad(guid: PlanetSideGUID): Boolean = features.Squad.GUID == guid
|
||||||
|
|
||||||
|
def appliesToSquadAndPosition(guid: PlanetSideGUID, squadPosition: Int): Boolean = appliesToSquad(guid) && position == squadPosition
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue