7 Commits

7 changed files with 91 additions and 29 deletions

View File

@@ -6,6 +6,8 @@
var websocket;
var transitionMap;
const transitionQueue = [];
let transitionInProgress = false;
var currentScreen = "blank";
var redSide;
var blueSide;
@@ -36,24 +38,47 @@ const bracketLogoScale = 0.75;
// Handles a websocket message to change which screen is displayed.
var handleAudienceDisplayMode = function(targetScreen) {
if (targetScreen === currentScreen) {
transitionQueue.push(targetScreen);
executeTransitionQueue();
};
// Sequentially executes all transitions in the queue. Returns without doing anything if another invocation is already
// in progress.
const executeTransitionQueue = function() {
if (transitionInProgress) {
// There is an existing invocation of this method which will execute all transitions in the queue.
return;
}
if (targetScreen === "sponsor") {
initializeSponsorDisplay();
}
transitions = transitionMap[currentScreen][targetScreen];
if (transitions == null) {
// There is no direct transition defined; need to go to the blank screen first.
transitions = function() {
transitionMap[currentScreen]["blank"](transitionMap["blank"][targetScreen]);
if (transitionQueue.length > 0) {
transitionInProgress = true;
const targetScreen = transitionQueue.shift();
const callback = function() {
// When the current transition is complete, call this method again to invoke the next one in the queue.
currentScreen = targetScreen;
transitionInProgress = false;
setTimeout(executeTransitionQueue, 100); // A small delay is needed to avoid visual glitches.
};
}
transitions();
currentScreen = targetScreen;
if (targetScreen === currentScreen) {
callback();
return;
}
if (targetScreen === "sponsor") {
initializeSponsorDisplay();
}
let transitions = transitionMap[currentScreen][targetScreen];
if (transitions !== undefined) {
transitions(callback);
} else {
// There is no direct transition defined; need to go to the blank screen first.
transitionMap[currentScreen]["blank"](function() {
transitionMap["blank"][targetScreen](callback);
});
}
}
};
// Handles a websocket message to update the teams for the current match.

View File

@@ -138,6 +138,9 @@
stroke-miterlimit:10;
}
.matchblock.complete #background {
fill:#e8e8e8;
}
.matchblock.active #background {
fill:#444444;
}
@@ -178,6 +181,21 @@
text-anchor:middle;
}
.matchblock.complete.red-win .teamnum.b,
.matchblock.complete.blue-win .teamnum.r {
fill:#888888;
}
.matchblock.complete.red-win.active .teamnum.b,
.matchblock.complete.blue-win.active .teamnum.r {
fill:#cccccc;
}
.matchblock.complete.blue-win .alliancenum.r {
fill:#ffc8c8;
}
.matchblock.complete.red-win .alliancenum.b {
fill:#A9D6FF;
}
<!-- Match Positioning -->
.bracket_double #match_1_1 {transform: translate(114px, 158px);}
@@ -461,7 +479,7 @@
{{end}}
{{define "matchup"}}
<g id="match_{{.Round}}_{{.Group}}" class="matchblock {{if .IsActive}}active{{end}}">
<g id="match_{{.Round}}_{{.Group}}" class="matchblock {{if .IsActive}}active{{end}} {{if .IsComplete}}complete {{.SeriesLeader}}-win{{end}}">
<rect class="structure" id="background" y="23" width="205" height="130.452"/>
<rect class="red" y="23" width="45.567" height="66.319"/>
<rect class="blue" y="89.133" width="45.567" height="64.319"/>
@@ -470,27 +488,27 @@
<text id="series_status" x="203.9999" y="170.5669" class="{{.SeriesLeader}}">{{.SeriesStatus}}</text>
<text id="match_title" x="0" y="17.3691">{{.DisplayName}}</text>
{{if .RedAlliance}}
<text x="22" y="70" class="alliancenum">{{.RedAlliance.Id}}</text>
<text x="22" y="70" class="alliancenum r">{{.RedAlliance.Id}}</text>
{{if ge (len .RedAlliance.TeamIds) 3}}
<text x="86.7247" y="54.0281" class="teamnum">{{index .RedAlliance.TeamIds 0}}</text>
<text x="162.8365" y="54.0281" class="teamnum">{{index .RedAlliance.TeamIds 1}}</text>
<text x="86.7247" y="81.2683" class="teamnum">{{index .RedAlliance.TeamIds 2}}</text>
<text x="86.7247" y="54.0281" class="teamnum r">{{index .RedAlliance.TeamIds 0}}</text>
<text x="162.8365" y="54.0281" class="teamnum r">{{index .RedAlliance.TeamIds 1}}</text>
<text x="86.7247" y="81.2683" class="teamnum r">{{index .RedAlliance.TeamIds 2}}</text>
{{end}}
{{if ge (len .RedAlliance.TeamIds) 4}}
<text x="162.8365" y="81.2683" class="teamnum">{{index .RedAlliance.TeamIds 3}}</text>
<text x="162.8365" y="81.2683" class="teamnum r">{{index .RedAlliance.TeamIds 3}}</text>
{{end}}
{{else}}
<text class="placeholder" x="101.1501" y="66.5769">{{.RedAllianceSource}}</text>
{{end}}
{{if .BlueAlliance}}
<text x="22" y="135" class="alliancenum">{{.BlueAlliance.Id}}</text>
<text x="22" y="135" class="alliancenum b">{{.BlueAlliance.Id}}</text>
{{if ge (len .BlueAlliance.TeamIds) 3}}
<text x="86.7247" y="119.1797" class="teamnum">{{index .BlueAlliance.TeamIds 0}}</text>
<text x="162.8365" y="119.1797" class="teamnum">{{index .BlueAlliance.TeamIds 1}}</text>
<text x="86.7247" y="146.4199" class="teamnum">{{index .BlueAlliance.TeamIds 2}}</text>
<text x="86.7247" y="119.1797" class="teamnum b">{{index .BlueAlliance.TeamIds 0}}</text>
<text x="162.8365" y="119.1797" class="teamnum b">{{index .BlueAlliance.TeamIds 1}}</text>
<text x="86.7247" y="146.4199" class="teamnum b">{{index .BlueAlliance.TeamIds 2}}</text>
{{end}}
{{if ge (len .BlueAlliance.TeamIds) 4}}
<text x="162.8365" y="146.4199" class="teamnum">{{index .BlueAlliance.TeamIds 3}}</text>
<text x="162.8365" y="146.4199" class="teamnum b">{{index .BlueAlliance.TeamIds 3}}</text>
{{end}}
{{else}}
<text class="placeholder" x="101.1501" y="130.4177">{{.BlueAllianceSource}}</text>

View File

@@ -44,8 +44,8 @@
<td class="text-center blue-text">
{{index $match.BlueTeams 0}}, {{index $match.BlueTeams 1}}, {{index $match.BlueTeams 2}}
</td>
<td class="text-center red-text">{{$match.RedScore}}</td>
<td class="text-center blue-text">{{$match.BlueScore}}</td>
<td class="text-center red-text">{{if $match.IsComplete}}{{$match.RedScore}}{{end}}</td>
<td class="text-center blue-text">{{if $match.IsComplete}}{{$match.BlueScore}}{{end}}</td>
<td class="text-center nowrap">
<a href="/match_review/{{$match.Id}}/edit"><b class="btn btn-info btn-xs">Edit</b></a>
</td>

View File

@@ -76,7 +76,13 @@ func (web *Web) allianceSelectionPostHandler(w http.ResponseWriter, r *http.Requ
}
}
if !found {
web.renderAllianceSelection(w, r, fmt.Sprintf("Team %d is not present at this event.", teamId))
web.renderAllianceSelection(
w,
r,
fmt.Sprintf(
"Team %d has not played any matches at this event and is ineligible for selection.", teamId,
),
)
return
}
}
@@ -248,7 +254,13 @@ func (web *Web) allianceSelectionFinalizeHandler(w http.ResponseWriter, r *http.
// Signal displays of the bracket to update themselves.
web.arena.ScorePostedNotifier.Notify()
http.Redirect(w, r, "/alliance_selection", 303)
// Load the first playoff match.
matches, err := web.arena.Database.GetMatchesByType("elimination")
if err == nil && len(matches) > 0 {
_ = web.arena.LoadMatch(&matches[0])
}
http.Redirect(w, r, "/match_play", 303)
}
// Publishes the alliances to the web.

View File

@@ -118,7 +118,7 @@ func TestAllianceSelectionErrors(t *testing.T) {
assert.Contains(t, recorder.Body.String(), "Invalid team number")
recorder = web.postHttpResponse("/alliance_selection", "selection0_0=100")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "not present at this event")
assert.Contains(t, recorder.Body.String(), "ineligible for selection")
recorder = web.postHttpResponse("/alliance_selection", "selection0_0=101&selection1_1=101")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "already part of an alliance")

View File

@@ -46,6 +46,7 @@ type allianceMatchup struct {
IsActive bool
SeriesLeader string
SeriesStatus string
IsComplete bool
}
// Generates a JSON dump of the matches and results.
@@ -250,6 +251,7 @@ func (web *Web) generateBracketSvg(w io.Writer) error {
DisplayName: matchup.LongDisplayName(),
RedAllianceSource: matchup.RedAllianceSourceDisplayName(),
BlueAllianceSource: matchup.BlueAllianceSourceDisplayName(),
IsComplete: matchup.IsComplete(),
}
if matchup.RedAllianceId > 0 {
if len(alliances) > 0 {

View File

@@ -24,6 +24,7 @@ type MatchReviewListItem struct {
RedScore int
BlueScore int
ColorClass string
IsComplete bool
}
// Shows the match review interface.
@@ -196,12 +197,16 @@ func (web *Web) buildMatchReviewList(matchType string) ([]MatchReviewListItem, e
switch match.Status {
case game.RedWonMatch:
matchReviewList[i].ColorClass = "danger"
matchReviewList[i].IsComplete = true
case game.BlueWonMatch:
matchReviewList[i].ColorClass = "info"
matchReviewList[i].IsComplete = true
case game.TieMatch:
matchReviewList[i].ColorClass = "warning"
matchReviewList[i].IsComplete = true
default:
matchReviewList[i].ColorClass = ""
matchReviewList[i].IsComplete = false
}
}