separated specific types of invitations and some of the messaging logic associated with them into separate classes; added custom messages everywhere; reorganized methods in an effort to improve code readability; light on the testing

This commit is contained in:
Fate-JH 2024-09-24 04:51:19 -04:00
parent 014e0e88ed
commit 2871b20182
14 changed files with 1094 additions and 514 deletions

View file

@ -363,8 +363,8 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
sendResponse(SquadWaypointEvent.Remove(ops.squad_supplement_id, char_id, waypoint_type))
case SquadResponse.SquadRelatedComment(comment) =>
sendResponse(ChatMsg(ChatMessageType.UNK_227, comment))
case SquadResponse.SquadRelatedComment(comment, messageType) =>
sendResponse(ChatMsg(messageType, comment))
case _ => ()
}

View file

@ -194,8 +194,8 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
sendResponse(SquadWaypointEvent.Remove(ops.squad_supplement_id, char_id, waypoint_type))
case SquadResponse.SquadRelatedComment(comment) =>
sendResponse(ChatMsg(ChatMessageType.UNK_227, comment))
case SquadResponse.SquadRelatedComment(comment, messageType) =>
sendResponse(ChatMsg(messageType, comment))
case _ => ()
}

View file

@ -1879,7 +1879,7 @@ class ZoningOperations(
private[session] var respawnTimer: Cancellable = Default.Cancellable
private var queuedActivities: Seq[SpawnOperations.ActivityQueuedTask] = Seq()
private var initialActivityDelay: Int = 4
private val initialActivityDelay: Int = 4
private var nextActivityDelay: Int = 0
private var statisticsPacketFunc: () => Unit = loginAvatarStatisticsFields
@ -1890,6 +1890,7 @@ class ZoningOperations(
val ReleaseAvatarRequestMessage() = pkt
log.info(s"${player.Name} on ${continent.id} has released")
reviveTimer.cancel()
avatarActive = false
GoToDeploymentMap()
HandleReleaseAvatar(player, continent)
}
@ -3733,6 +3734,7 @@ class ZoningOperations(
zoningStatus = Zoning.Status.None
player.death_by = math.min(player.death_by, 0)
player.allowInteraction = true
avatarActive = true
nextSpawnPoint.foreach { tube =>
sendResponse(PlayerStateShiftMessage(ShiftState(0, tube.Position, tube.Orientation.z)))
nextSpawnPoint = None

View file

@ -271,7 +271,7 @@ class SquadService extends Actor {
SquadActionMembershipDisband(char_id)
case SquadAction.Membership(SquadRequestType.Cancel, cancellingPlayer, _, _, _) =>
SquadActionMembershipCancel(cancellingPlayer)
SquadActionMembershipCancel(cancellingPlayer, tplayer)
case SquadAction.Membership(SquadRequestType.Promote, _, _, _, _) => ()
// case SquadAction.Membership(SquadRequestType.Promote, promotingPlayer, Some(_promotedPlayer), promotedName, _) =>
@ -329,8 +329,10 @@ class SquadService extends Actor {
out
} else {
//this isn't the squad we're looking for by GUID; as a precaution, reload all of the published squad list
val charId = tplayer.CharId
val faction = tplayer.Faction
subs.Publish(faction, SquadResponse.InitList(PublishedLists(tplayer.Faction)))
searchData.remove(charId)
subs.Publish(charId, SquadResponse.InitList(PublishedLists(faction)))
None
}
case _ =>
@ -538,7 +540,7 @@ class SquadService extends Actor {
if invitersFeatures.Squad.Size == 1 =>
//both players belong to squads, but the invitingPlayer's squad is underutilized by comparison
//treat the same as "indirection ..." using squad2
invitations.createIndirectInvite(tplayer, invitedPlayer, invitedFeatures)
invitations.createPermissionToRedirectInvite(tplayer, invitedPlayer, invitedFeatures)
case (Some(features), None) =>
//the classic situation
@ -546,7 +548,7 @@ class SquadService extends Actor {
case (None, Some(features)) =>
//indirection; we're trying to invite ourselves to someone else's squad
invitations.createIndirectInvite(tplayer, invitedPlayer, features)
invitations.createPermissionToRedirectInvite(tplayer, invitedPlayer, features)
case (None, None) =>
//neither the invited player nor the inviting player belong to any squad
@ -631,9 +633,13 @@ class SquadService extends Actor {
.foreach(features => DisbandSquad(features))
}
def SquadActionMembershipCancel(cancellingPlayer: Long): Unit = {
def SquadActionMembershipCancel(cancellingPlayer: Long, player: Player): Unit = {
//get rid of SpontaneousInvite objects and VacancyInvite objects
invitations.handleCancelling(cancellingPlayer)
invitations.handleCancelling(
cancellingPlayer,
player,
GetLeadingSquad(cancellingPlayer, None)
)
}
def SquadActionMembershipPromote(
@ -729,10 +735,8 @@ class SquadService extends Actor {
charId: Long,
criteria: SquadService.SearchCriteria
): Unit = {
subs.Publish(
charId,
SquadResponse.SquadSearchResults(SearchForSquadsResults(criteria))
)
subs.Publish(charId, SquadResponse.InitList(PublishedLists(criteria.faction)))
subs.Publish(charId, SquadResponse.SquadSearchResults(SearchForSquadsResults(criteria)))
}
private def SearchForSquadsResults(criteria: SquadService.SearchCriteria): List[PlanetSideGUID] = {
@ -950,7 +954,7 @@ class SquadService extends Actor {
* will still leave the squad, but will not attempt to send feedback to the said unreachable client.
* If the player is in the process of unsubscribing from the service,
* the no-messaging pathway is useful to avoid accumulating dead letters.
* @see `CleanUpAllInvitesToSquad`
* @see `CleanUpAllInvitesForSquad`
* @see `SquadDetail`
* @see `SquadSubscriptionEntity.Publish`
* @see `TryResetSquadId`

View file

@ -5,7 +5,7 @@ import akka.actor.ActorRef
import net.psforever.objects.avatar.Certification
import net.psforever.objects.teamwork.Squad
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
final case class SquadServiceResponse(channel: String, exclude: Iterable[Long], response: SquadResponse.Response)
@ -84,5 +84,5 @@ object SquadResponse {
zoneNumber: Int
) extends Response
final case class SquadRelatedComment(str: String) extends Response
final case class SquadRelatedComment(str: String, messageType: ChatMessageType = ChatMessageType.UNK_227) extends Response
}

View file

@ -3,29 +3,30 @@ 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.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.
* Depending on the situation, either the squad leader or the player who would join the squad handle this invitation.
*
* @param recruitOrOwner player who would be joining the squad;
* may or may not have actually requested it in the first place
* 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(recruitOrOwner: Player, features: SquadFeatures)
extends Invitation(recruitOrOwner.CharId, recruitOrOwner.Name) {
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, recruitOrOwner, invitedPlayer, invitingPlayer, otherName)
indirectInviteFunc(this, originalRequester, invitedPlayer, invitingPlayer, otherName)
}
def handleAcceptance(
@ -35,17 +36,59 @@ final case class IndirectInvite(recruitOrOwner: Player, features: SquadFeatures)
@unused invitedPlayerSquadOpt: Option[SquadFeatures]
): Unit = {
//tplayer / invitedPlayer is actually the squad leader
if (SquadInvitationManager.canEnrollInSquad(features, recruitOrOwner.CharId)) {
val recruitCharId = recruitOrOwner.CharId
manager.handleVacancyInvite(features, recruitCharId, invitedPlayer, recruitOrOwner) match {
case Some((_, line)) =>
manager.acceptanceMessages(invitedPlayer, recruitCharId, recruitOrOwner.Name)
manager.joinSquad(recruitOrOwner, features, line)
manager.cleanUpAllInvitesWithPlayer(recruitCharId)
manager.cleanUpInvitesForSquadAndPosition(features, line)
//TODO since we are the squad leader, we do not want to brush off our queued squad invite tasks
case _ => ()
}
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 {
}
}
@ -55,7 +98,15 @@ final case class IndirectInvite(recruitOrOwner: Player, features: SquadFeatures)
rejectingPlayer: Long,
@unused squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
): Unit = {
//todo how to do this?
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(
@ -63,16 +114,48 @@ final case class IndirectInvite(recruitOrOwner: Player, features: SquadFeatures)
player: Player,
rejectingPlayer: Long
): Unit = {
//todo how to do this?
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 = recruitOrOwner
def getPlayer: Player = originalRequester
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == recruitOrOwner.CharId
def appliesToPlayer(playerCharId: Long): Boolean = playerCharId == originalRequester.CharId
def appliesToSquad(guid: PlanetSideGUID): Boolean = features.Squad.GUID == guid

View file

@ -56,6 +56,19 @@ abstract class Invitation(charId: Long, name: String) {
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]

View file

@ -48,21 +48,50 @@ final case class InvitationToCreateASquad(futureSquadLeader: Player)
.askToCreateANewSquad(futureSquadLeader)
.onComplete {
case Success(FinishStartSquad(features)) =>
manager.handleVacancyInvite(features, invitedPlayer, leaderCharId, player) match {
case Some((_, line)) =>
manager.publish(
invitedPlayer,
SquadResponse.Membership(SquadResponseType.Accept, invitedPlayer, Some(leaderCharId), "", unk5 = true)
)
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.Membership(SquadResponseType.Accept, leaderCharId, Some(invitedPlayer), player.Name, unk5 = false)
SquadResponse.SquadRelatedComment(s"Though a squad has been created, a member could not join it.")
)
manager.joinSquad(player, features, line)
manager.cleanUpQueuedInvites(invitedPlayer)
case _ => ()
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 _ => ()
case _ =>
org.log4s.getLogger("InvitationToCreateASquad").error("could not create a squad when requested")
}
}
}
@ -98,6 +127,35 @@ final case class InvitationToCreateASquad(futureSquadLeader: Player)
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

View file

@ -46,14 +46,56 @@ final case class InvitationToJoinSquad(charId: Long, name: String, features: Squ
SquadInvitationManager.canEnrollInSquad(features, invitedPlayer)
) {
//accepted an invitation to join an existing squad
manager.handleVacancyInvite(features, invitedPlayer, charId, player) match {
case Some((_, line)) =>
manager.acceptanceMessages(charId, invitedPlayer, player.Name)
manager.joinSquad(player, features, line)
manager.cleanUpQueuedInvites(invitedPlayer)
manager.cleanUpInvitesForSquadAndPosition(features, line)
case _ => ()
}
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
}
}
}
@ -81,6 +123,36 @@ final case class InvitationToJoinSquad(charId: Long, name: String, features: Squ
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)

View file

@ -2,7 +2,7 @@
package net.psforever.services.teamwork.invitations
import net.psforever.objects.teamwork.{Member, SquadFeatures}
import net.psforever.objects.{LivePlayerList, Player}
import net.psforever.objects.Player
import net.psforever.services.teamwork.{SquadInvitationManager, SquadResponse}
import net.psforever.types.{PlanetSideGUID, SquadResponseType}
@ -38,18 +38,64 @@ final case class LookingForSquadRoleInvite(squadLeader: Member, features: SquadF
invitedPlayer: Long,
@unused invitedPlayerSquadOpt: Option[SquadFeatures]
): Unit = {
val leaderCharId = squadLeader.CharId
if (
manager.notLimitedByEnrollmentInSquad(invitedPlayerSquadOpt, invitedPlayer) &&
SquadInvitationManager.canEnrollInSquad(features, invitedPlayer)
SquadInvitationManager.canEnrollInSquad(features, invitedPlayer) &&
manager.joinSquad(player, features, position)
) {
val invitingPlayer = squadLeader.CharId
features.ProxyInvites = features.ProxyInvites.filterNot { _ == invitedPlayer }
if (manager.joinSquad(player, features, position)) {
//join this squad
manager.acceptanceMessages(invitingPlayer, invitedPlayer, player.Name)
manager.cleanUpQueuedInvites(player.CharId)
manager.cleanUpInvitesForSquadAndPosition(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.")
)
}
}
@ -74,14 +120,45 @@ final case class LookingForSquadRoleInvite(squadLeader: Member, features: SquadF
player: Player,
rejectingPlayer: Long
): Unit = {
val faction = player.Faction
manager.reloadSearchForRoleInvite(
LivePlayerList.WorldPopulation { _ => true },
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)

View file

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

View file

@ -44,26 +44,46 @@ final case class ProximityInvite(squadLeader: Member, features: SquadFeatures, p
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)
SquadInvitationManager.canEnrollInSquad(features, invitedPlayer) &&
manager.joinSquad(player, features, position)
) {
val invitingPlayer = squadLeader.CharId
features.ProxyInvites = features.ProxyInvites.filterNot { _ == invitedPlayer }
if (manager.joinSquad(player, features, position)) {
//join this squad
manager.acceptanceMessages(invitingPlayer, invitedPlayer, player.Name)
manager.cleanUpAllInvitesWithPlayer(invitedPlayer)
val squad = features.Squad
if (squad.Size == squad.Capacity) {
//all available squad positions filled; terminate all remaining invitations
manager.cleanUpAllInvitesToSquad(features)
//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.")
)
}
} else {
manager.reloadProximityInvite(player.Zone.Players, invitedPlayer, features, position) //TODO ?
(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(
@ -90,6 +110,31 @@ final case class ProximityInvite(squadLeader: Member, features: SquadFeatures, p
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)

View file

@ -36,12 +36,60 @@ final case class RequestToJoinSquadRole(requestee: Player, features: SquadFeatur
): 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.cleanUpInvitesForSquadAndPosition(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.")
)
}
}
@ -70,6 +118,38 @@ final case class RequestToJoinSquadRole(requestee: Player, features: SquadFeatur
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)