Enhance roster management by implementing cross-role player swapping and updating player movement logic. Introduce rating checks for player roles during roster operations and ensure proper handling of captain assignments. Update frontend components to reflect changes in player movement capabilities.
This commit is contained in:
@@ -147,7 +147,24 @@ func (s *Service) SwapRoster(ctx context.Context, actor domain.Account, eventID,
|
||||
if roster.Version != expectedVersion {
|
||||
return roster, fmt.Errorf("%w: stale roster version", domain.ErrConflict)
|
||||
}
|
||||
if err = roster.Swap(teamA, playerA, teamB, playerB); err != nil {
|
||||
roleA, roleB := rosterPlayerRole(roster, teamA, playerA), rosterPlayerRole(roster, teamB, playerB)
|
||||
players, err := s.Store.ListPlayers(ctx)
|
||||
if err != nil {
|
||||
return roster, err
|
||||
}
|
||||
ratingAForB, ratingBForA := 0, 0
|
||||
for _, player := range players {
|
||||
if player.ID == playerA {
|
||||
ratingAForB = ratingForRole(player, roleB)
|
||||
}
|
||||
if player.ID == playerB {
|
||||
ratingBForA = ratingForRole(player, roleA)
|
||||
}
|
||||
}
|
||||
if ratingAForB == 0 || ratingBForA == 0 {
|
||||
return roster, domain.ErrNotFound
|
||||
}
|
||||
if err = roster.SwapAcrossRoles(teamA, playerA, teamB, playerB, ratingBForA, ratingAForB); err != nil {
|
||||
return roster, err
|
||||
}
|
||||
roster, err = s.Store.SaveRoster(ctx, roster, expectedVersion)
|
||||
@@ -170,7 +187,21 @@ func (s *Service) MoveRosterPlayer(ctx context.Context, actor domain.Account, ev
|
||||
if roster.Version != expectedVersion {
|
||||
return roster, fmt.Errorf("%w: stale roster version", domain.ErrConflict)
|
||||
}
|
||||
if err = roster.MoveToEmpty(fromTeamID, playerID, toTeamID, role); err != nil {
|
||||
players, err := s.Store.ListPlayers(ctx)
|
||||
if err != nil {
|
||||
return roster, err
|
||||
}
|
||||
targetRating := 0
|
||||
for _, player := range players {
|
||||
if player.ID == playerID {
|
||||
targetRating = ratingForRole(player, role)
|
||||
break
|
||||
}
|
||||
}
|
||||
if targetRating == 0 {
|
||||
return roster, domain.ErrNotFound
|
||||
}
|
||||
if err = roster.MoveToEmpty(fromTeamID, playerID, toTeamID, role, targetRating); err != nil {
|
||||
return roster, err
|
||||
}
|
||||
return s.saveRosterChange(ctx, actor, roster, expectedVersion, "roster.player_moved")
|
||||
@@ -621,3 +652,17 @@ func ratingForRole(player domain.Player, role domain.Role) int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func rosterPlayerRole(roster domain.RosterDraft, teamID, playerID string) domain.Role {
|
||||
for _, team := range roster.Teams {
|
||||
if team.ID != teamID {
|
||||
continue
|
||||
}
|
||||
for _, slot := range team.Slots {
|
||||
if slot.PlayerID == playerID {
|
||||
return slot.Role
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -82,14 +82,17 @@ func (r *RosterDraft) Swap(teamAID, playerAID, teamBID, playerBID string) error
|
||||
return fmt.Errorf("%w: rosters are locked", ErrConflict)
|
||||
}
|
||||
var a, b *Slot
|
||||
var teamA, teamB *Team
|
||||
for teamIndex := range r.Teams {
|
||||
for slotIndex := range r.Teams[teamIndex].Slots {
|
||||
slot := &r.Teams[teamIndex].Slots[slotIndex]
|
||||
if r.Teams[teamIndex].ID == teamAID && slot.PlayerID == playerAID {
|
||||
a = slot
|
||||
teamA = &r.Teams[teamIndex]
|
||||
}
|
||||
if r.Teams[teamIndex].ID == teamBID && slot.PlayerID == playerBID {
|
||||
b = slot
|
||||
teamB = &r.Teams[teamIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,10 +104,52 @@ func (r *RosterDraft) Swap(teamAID, playerAID, teamBID, playerBID string) error
|
||||
}
|
||||
a.PlayerID, b.PlayerID = b.PlayerID, a.PlayerID
|
||||
a.Rating, b.Rating = b.Rating, a.Rating
|
||||
clearMovedCaptains(teamA, playerAID, teamB, playerBID)
|
||||
r.Version++
|
||||
return r.Validate(false)
|
||||
}
|
||||
|
||||
func (r *RosterDraft) SwapAcrossRoles(teamAID, playerAID, teamBID, playerBID string, playerBRatingForA, playerARatingForB int) error {
|
||||
if r.Confirmed {
|
||||
return fmt.Errorf("%w: rosters are locked", ErrConflict)
|
||||
}
|
||||
var a, b *Slot
|
||||
var teamA, teamB *Team
|
||||
for teamIndex := range r.Teams {
|
||||
for slotIndex := range r.Teams[teamIndex].Slots {
|
||||
slot := &r.Teams[teamIndex].Slots[slotIndex]
|
||||
if r.Teams[teamIndex].ID == teamAID && slot.PlayerID == playerAID {
|
||||
a = slot
|
||||
teamA = &r.Teams[teamIndex]
|
||||
}
|
||||
if r.Teams[teamIndex].ID == teamBID && slot.PlayerID == playerBID {
|
||||
b = slot
|
||||
teamB = &r.Teams[teamIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
if a == nil || b == nil {
|
||||
return ErrNotFound
|
||||
}
|
||||
a.PlayerID, b.PlayerID = b.PlayerID, a.PlayerID
|
||||
a.Rating, b.Rating = playerBRatingForA, playerARatingForB
|
||||
clearMovedCaptains(teamA, playerAID, teamB, playerBID)
|
||||
r.Version++
|
||||
return r.Validate(false)
|
||||
}
|
||||
|
||||
func clearMovedCaptains(teamA *Team, playerAID string, teamB *Team, playerBID string) {
|
||||
if teamA == nil || teamB == nil || teamA.ID == teamB.ID {
|
||||
return
|
||||
}
|
||||
if teamA.CaptainPlayerID == playerAID {
|
||||
teamA.CaptainPlayerID = ""
|
||||
}
|
||||
if teamB.CaptainPlayerID == playerBID {
|
||||
teamB.CaptainPlayerID = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RosterDraft) Substitute(teamID, outgoingID, reserveID string, rating int) error {
|
||||
if r.Confirmed {
|
||||
return fmt.Errorf("%w: rosters are locked", ErrConflict)
|
||||
@@ -158,7 +203,7 @@ func (r *RosterDraft) PlaceReserve(teamID string, role Role, reserveID string, r
|
||||
return fmt.Errorf("%w: empty role slot not found", ErrNotFound)
|
||||
}
|
||||
|
||||
func (r *RosterDraft) MoveToEmpty(fromTeamID, playerID, toTeamID string, role Role) error {
|
||||
func (r *RosterDraft) MoveToEmpty(fromTeamID, playerID, toTeamID string, role Role, targetRating int) error {
|
||||
if r.Confirmed {
|
||||
return fmt.Errorf("%w: rosters are locked", ErrConflict)
|
||||
}
|
||||
@@ -179,10 +224,7 @@ func (r *RosterDraft) MoveToEmpty(fromTeamID, playerID, toTeamID string, role Ro
|
||||
if source == nil || target == nil {
|
||||
return ErrNotFound
|
||||
}
|
||||
if source.Role != target.Role {
|
||||
return fmt.Errorf("%w: only equal roles can be moved", ErrInvalid)
|
||||
}
|
||||
target.PlayerID, target.Rating = source.PlayerID, source.Rating
|
||||
target.PlayerID, target.Rating = source.PlayerID, targetRating
|
||||
source.PlayerID, source.Rating = "", 0
|
||||
if sourceTeam.CaptainPlayerID == playerID {
|
||||
sourceTeam.CaptainPlayerID = ""
|
||||
|
||||
@@ -67,12 +67,20 @@ func TestRosterDragOperationsPreserveEmptyRoleSlots(t *testing.T) {
|
||||
if err := roster.MoveToReserve("b", "b-t"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := roster.MoveToEmpty("a", "reserve", "b", Tank); err != nil {
|
||||
if err := roster.MoveToEmpty("a", "reserve", "b", Tank, 31); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if roster.Teams[1].Slots[0].PlayerID != "reserve" {
|
||||
t.Fatal("dragging into an empty role slot did not move the player")
|
||||
}
|
||||
roster.Teams[0].CaptainPlayerID = "a-d1"
|
||||
roster.Teams[1].CaptainPlayerID = "reserve"
|
||||
if err := roster.SwapAcrossRoles("a", "a-d1", "b", "reserve", 25, 33); err != nil {
|
||||
t.Fatalf("cross-role drag swap failed: %v", err)
|
||||
}
|
||||
if roster.Teams[0].CaptainPlayerID != "" || roster.Teams[1].CaptainPlayerID != "" {
|
||||
t.Fatal("captains that moved to another team were not cleared")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeriesFSMRunsCoinBansAndResult(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user