mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
60 lines
1.9 KiB
Go
60 lines
1.9 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
type CasualPhase string
|
|
|
|
const (
|
|
CasualKickoff CasualPhase = "KICKOFF"
|
|
CasualLive CasualPhase = "LIVE"
|
|
)
|
|
|
|
type CasualSlot struct {
|
|
Slot int
|
|
Team int
|
|
PlayerID string
|
|
IsBot bool
|
|
}
|
|
|
|
// BuildCasualLineup freezes the live six-slot shape. Missing humans become
|
|
// explicit server bots; no human is inserted after live play begins by this
|
|
// function.
|
|
func BuildCasualLineup(participants []ConnectParticipant) ([]CasualSlot, error) {
|
|
if len(participants) < 2 || len(participants) > 6 {
|
|
return nil, fmt.Errorf("casual lineup needs 2 through 6 humans")
|
|
}
|
|
seen := make(map[string]bool, len(participants))
|
|
teamHuman := map[int]bool{}
|
|
lineup := make([]CasualSlot, 6)
|
|
usedSlots := make(map[int]bool)
|
|
for _, participant := range participants {
|
|
if participant.PlayerID == "" || participant.Team < 0 || participant.Team > 1 || participant.Slot < 0 || participant.Slot > 5 || participant.Slot/3 != participant.Team || seen[participant.PlayerID] || usedSlots[participant.Slot] {
|
|
return nil, fmt.Errorf("invalid casual participant")
|
|
}
|
|
seen[participant.PlayerID] = true
|
|
usedSlots[participant.Slot] = true
|
|
teamHuman[participant.Team] = true
|
|
lineup[participant.Slot] = CasualSlot{Slot: participant.Slot, Team: participant.Team, PlayerID: participant.PlayerID}
|
|
}
|
|
if !teamHuman[0] || !teamHuman[1] {
|
|
return nil, fmt.Errorf("casual lineup requires one human on each team")
|
|
}
|
|
for i := range lineup {
|
|
if lineup[i].PlayerID == "" {
|
|
lineup[i] = CasualSlot{Slot: i, Team: i / 3, PlayerID: fmt.Sprintf("bot-slot-%d", i), IsBot: true}
|
|
}
|
|
}
|
|
return lineup, nil
|
|
}
|
|
|
|
func CanCasualBackfill(phase CasualPhase, slot CasualSlot) bool {
|
|
return phase == CasualKickoff && slot.IsBot
|
|
}
|
|
|
|
// CasualBackfillPenalty is intentionally zero: a kickoff-only backfill does
|
|
// not receive a hidden-rating update or an abandon/decline cooldown.
|
|
func CasualBackfillPenalty() time.Duration { return 0 }
|