Skip to content

Commit

Permalink
Box clocks started when jam isn't running now go into paused mode and…
Browse files Browse the repository at this point in the history
… wait for jam start

* Addressing user feedback, changed behavior of clocks started between jams. Previously, they would start counting and it was the responsibility of the user to reset them before the next jam. Now, when clocks are started between jams they don't start counting yet. Instead the clock is indicated to be active by changing the color from gray to white and disabling the start button. They look the same way as when paused between jams. When the jam starts the clock will start counting down. This accomadates the scenario where a skater sits in the box between jams; the penalty timer can go ahead and hit start for them, and it'll get their clock ready to start at the next jam.
* When penalty box clocks are paused at the end of a jam, they now stay white and the start button is dimmed, to make it more clear that they're still active and will resume again at the next jam start.
* Fixed scenario where both jammers are put in box between jams. Their timers will set to zero, or if they have different numbers of penalties those will reduce as far as possible, so it's clear how much time they will have on the clock when the jam starts.
* Fixed bug where adding extra time to a clock before starting wasn't working; it would get reset back to default penalty time at start.
* Fixed some bugs in jammer logic. If jammers have multiple penalties we now cancel extra unserved penalties as much as possible.
  • Loading branch information
katpet committed Oct 5, 2023
1 parent 20e5a64 commit e144773
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 88 deletions.
110 changes: 89 additions & 21 deletions html/components/pbt-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ const TimerControl = {
Reset: "Reset"
}

const Colors = {
Paused: "white",
Alert: "crimson",
Stop: "crimson",
Inactive: "dimgrey",
Active: "white",
Start: "limegreen"
}

function isTrue(value) {
'use strict';
if (typeof value === 'boolean') {
Expand Down Expand Up @@ -295,39 +304,98 @@ function setupCallbacks() {
);

WS.Register(
'ScoreBoard.CurrentGame.BoxClock(*).Time',
[
'ScoreBoard.CurrentGame.BoxClock(*).Time',
'ScoreBoard.CurrentGame.Team(*).BoxSeat(*).Started',
'ScoreBoard.CurrentGame.Clock(Jam).Running',
],
function (k, v) {
var running = isTrue(WS.state['ScoreBoard.CurrentGame.BoxClock(' + k.BoxClock + ').Running']);
if(running) {
var running = isTrue(WS.state['ScoreBoard.CurrentGame.BoxClock(' + k.BoxClock + ').Running']);
if(running) {
if(v <= 10000) {
$('.Time.'+k.BoxClock).css('color', 'crimson');
$('.Time.'+k.BoxClock).css('color', Colors.Alert);
} else {
$('.Time.'+k.BoxClock).css('color', 'white');
$('.Time.'+k.BoxClock).css('color', Colors.Active);
}
} else if(k.BoxClock) {
var jamrunning = isTrue(WS.state['ScoreBoard.CurrentGame.Clock(Jam).Running']);
var team = k.BoxClock.startsWith('Team2') ? 2 : 1;
var pos = '';
for(const p of ["Jammer", "Blocker1", "Blocker2", "Blocker3"]) {
if(k.BoxClock.endsWith(p)) {
pos = p;
}
}

var started = isTrue(WS.state['ScoreBoard.CurrentGame.Team('+team+').BoxSeat('+ pos+').Started']);
if(!jamrunning && started) {
// indicate a clock that is paused between jams so it's clearer it'll start back up on next jam
$('.Time.'+k.BoxClock).css('color', Colors.Paused);
}
} else if(k.BoxSeat) {
if(v) {
$('.Time.Team' + k.Team + k.BoxSeat).css('color', Colors.Paused);
var jamrunning = isTrue(WS.state['ScoreBoard.CurrentGame.Clock(Jam).Running']);
if(!jamrunning) {
$('.BoxTimerPage button.Team'+k.Team + k.BoxSeat+'.Start').prop("disabled", true);
}
} else {
$('.Time.Team' + k.Team + k.BoxSeat).css('color', Colors.Inactive);
$('.BoxTimerPage button.Team'+k.Team + k.BoxSeat+'.Start').prop("disabled", false);
}
} else {
$('.Time.'+k.BoxClock).css('color', 'gray');
}
}
);

WS.Register(
'ScoreBoard.CurrentGame.BoxClock(*).Running',
[
'ScoreBoard.CurrentGame.BoxClock(*).Running',
'ScoreBoard.CurrentGame.Clock(Jam).Running',
'ScoreBoard.CurrentGame.Team(*).BoxSeat(*).Started',
],
function (k, v) {
if(isTrue(v)) {
var running = isTrue(WS.state['ScoreBoard.CurrentGame.BoxClock(' + k.BoxClock + ').Running']);
if(k.BoxClock == null) {
return;
}
if(running) {
$('.BoxTimerPage button.'+k.BoxClock+'.Stop').show();
$('.BoxTimerPage button.'+k.BoxClock+'.Start').hide();
$('.BoxTimerPage button.'+k.BoxClock+'.Reset').prop("disabled", true);
$('.Time.'+k.BoxClock).css('color', 'white');
$('.Time.'+k.BoxClock).css('color', Colors.Active);
} else {
$('.BoxTimerPage button.'+k.BoxClock+'.Stop').hide();
$('.BoxTimerPage button.'+k.BoxClock+'.Start').show();
$('.BoxTimerPage button.'+k.BoxClock+'.Start').prop("disabled", false);
$('.BoxTimerPage button.'+k.BoxClock+'.Reset').prop("disabled", false);
$('.Time.'+k.BoxClock).css('color', 'dimgray');
}

var jamrunning = isTrue(WS.state['ScoreBoard.CurrentGame.Clock(Jam).Running']);
var team = k.BoxClock.startsWith('Team2') ? 2 : 1;
var pos = '';

for(const p of ["Jammer", "Blocker1", "Blocker2", "Blocker3"]) {
if(k.BoxClock.endsWith(p)) {
pos = p;
}
}
var started = isTrue(WS.state['ScoreBoard.CurrentGame.Team('+team+').BoxSeat('+pos+').Started']);
if(started) {
$('.Time.'+k.BoxClock).css('color', Colors.Paused);
if(!jamrunning) {
$('.BoxTimerPage button.'+k.BoxClock+'.Start').prop("disabled", true);
}
} else {
$('.Time.'+k.BoxClock).css('color', Colors.Inactive);
$('.BoxTimerPage button.'+k.BoxClock+'.Start').prop("disabled", false);
}
}
}
);

WS.Register('ScoreBoard.CurrentGame.Team(*).Skater(*).Role',
WS.Register(
[
'ScoreBoard.CurrentGame.Team(*).Skater(*).Role',
],
function (k, v) {
if(v == Position.Jammer) {
var selectId = $('#PenaltyBoxJammersTeam'+k.Team+'DisplayJammer,#PenaltyBoxBothTeamsTeam'+k.Team+'DisplayJammer');
Expand All @@ -341,7 +409,7 @@ function setupCallbacks() {
WS.Register(
[
'ScoreBoard.CurrentGame.Team(*).BoxSeat(*).BoxSkaterPenalties',
],
],
function (k, v) {
var sel = $('.Team'+k.Team+'.'+k.BoxSeat+'.PenaltyCount');
if(sel.length) {
Expand All @@ -360,7 +428,7 @@ WS.Register([
function(k, v) {
// Edit the popup that allows adding/removing time to match penalty duration rule
var sel = $('.editTime');
if(sel.length) {
if(sel.length && v) {

// Convert from format like 0:30 or 1:00 to seconds,
// which appears in menu like '+30', '-30' or '+60', '-60'
Expand Down Expand Up @@ -437,12 +505,12 @@ WS.Register(
}

function setupButtonsStyle() {
$('.BoxTimerPage button.Start').css('background-color', 'limegreen');
$('.BoxTimerPage button.Start').css('color', 'white');
$('.BoxTimerPage button.Stop').css('background-color', 'crimson');
$('.BoxTimerPage button.Stop').css('color', 'white');
$('.BoxTimerPage button.Reset').css('background-color', 'dimgray');
$('.BoxTimerPage button.Reset').css('color', 'white');
$('.BoxTimerPage button.Start').css('background-color', Colors.Start);
$('.BoxTimerPage button.Start').css('color', Colors.Active);
$('.BoxTimerPage button.Stop').css('background-color', Colors.Stop);
$('.BoxTimerPage button.Stop').css('color', Colors.Active);
$('.BoxTimerPage button.Reset').css('background-color', Colors.Inactive);
$('.BoxTimerPage button.Reset').css('color', Colors.Active);
}

function setupEditButton(pageId, teamId, pos, seatId) {
Expand Down
161 changes: 99 additions & 62 deletions src/com/carolinarollergirls/scoreboard/core/game/BoxSeatImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ public void setSkater(Skater s) {

@Override
public void startBox() {
Clock jc = team.getGame().getClock(Clock.ID_JAM);
if(started()) {
if(!jc.isRunning()) {
return;
}
// Resume a clock that was paused.
synchronized (coreLock) {
Clock pc = getBoxClock();
Expand Down Expand Up @@ -109,6 +113,92 @@ public void startBox() {
}
}
restartBox();
if(!jc.isRunning()) {
stopBox(); // If jam not running, pause right away
getBoxClock().resetTime();
}
}
}

private void doJammerLogic(Clock pc) {
if(getFloorPosition() != FloorPosition.JAMMER) {
return;
}
Team otherTeam = (team.getProviderId() == Team.ID_1) ? team.getGame().getTeam(Team.ID_2) : team.getGame().getTeam(Team.ID_1);
for(BoxSeat bs : otherTeam.getAll(Team.BOX_SEAT)) {
if(bs.getFloorPosition() == FloorPosition.JAMMER) {
Clock otherClock = bs.getBoxClock();
if(otherClock != null && (otherClock.isRunning() || bs.started())) {
long penaltyDuration = team.getGame().getLong(Rule.PENALTIES_DURATION);
long otherMaximum = otherClock.getMaximumTime();
long thisMaximum = pc.getMaximumTime();
long otherTime = otherClock.getTime();
long thisTime = pc.getTime();
long otherElapsed2 = otherMaximum - otherTime;
long epsilon = 800;
if(otherElapsed2 < epsilon) {
// This value can end up being not quite zero or even a bit negative, which is undesirable
// because then it'll be ignored when we try to set the clock value, so round to 0 when approximately 0.
// But, use a small number of milliseconds here to represent zero, because
// that will trigger the UI to notice that the clock has changed; it'll still show as 0 seconds.
//otherElapsed2 = 1;
}
long otherNewTime = otherTime;
long thisNewTime = thisMaximum;

if(otherNewTime >= penaltyDuration || thisNewTime >= penaltyDuration) {
// A jammer has multiple penalties.
// Cancel extra penalties as far as we can, taking into account how much the other jammer has already served.
// Since these times aren't exact, check within some small amount of penalty length.
while(otherNewTime >= (penaltyDuration - epsilon) && thisNewTime >= (penaltyDuration - epsilon)) {
otherNewTime -= penaltyDuration;
thisNewTime -= penaltyDuration;
otherMaximum -= penaltyDuration;
thisMaximum -= penaltyDuration;
}
}

if(otherMaximum == 0 || thisMaximum == 0) {
// If a max reduced to zero above, the values for thisNewTime and otherNewTime are already good
} else if(otherMaximum == thisMaximum) {
// Simple, common case.
// Sitting jammer is released,
// and arriving jammer serves as much as sitting jammer did.
thisNewTime = otherElapsed2;
otherNewTime = 1;
} else if(otherMaximum > penaltyDuration && thisMaximum == penaltyDuration) {
// This and next case are similar to the simple one above except the max times differ,
// so need to account for that in the elapsed time.
thisNewTime = otherElapsed2 - (otherMaximum - thisMaximum);
otherNewTime = 1;
} else if(otherMaximum == penaltyDuration && thisMaximum > penaltyDuration) {
thisNewTime = otherElapsed2 + (thisMaximum - otherMaximum);
otherNewTime = 1;
} else {
// The nightmare scenario!
// A jammer has returned to the box while
// sitting jammer is already serving a reduced single penalty,
// so can't reduce that any more.
// Sitting jammer must finish their penalty,
// and arriving jammer gets regular penalty.
otherNewTime = otherTime;
thisNewTime = penaltyDuration;
}

// jigger refresh of clock
if(thisNewTime == 0) {
thisNewTime = epsilon;
}
if(thisNewTime == thisTime) {
thisNewTime += 1;
}
pc.setMaximumTime(thisNewTime);
pc.setTime(thisNewTime);
otherClock.setTime(otherNewTime);
pc.restart();
}
break;
}
}
}

Expand All @@ -119,68 +209,11 @@ private void restartBox() {
if(pc == null) {
return;
}
pc.setMaximumTime(team.getGame().getLong(Rule.PENALTIES_DURATION));
pc.setCountDirectionDown(true);
pc.restart();
setWallStartTime(ScoreBoardClock.getInstance().getCurrentWalltime());
if(hasFloorPosition()) {
if(getFloorPosition() == FloorPosition.JAMMER) {
// Jammer logic
Team otherTeam = (team.getProviderId() == Team.ID_1) ? team.getGame().getTeam(Team.ID_2) : team.getGame().getTeam(Team.ID_1);
for(BoxSeat bs : otherTeam.getAll(Team.BOX_SEAT)) {
if(bs.getFloorPosition() == FloorPosition.JAMMER) {
Clock otherClock = bs.getBoxClock();
if(otherClock != null && otherClock.isRunning()) {
long penaltyDuration = team.getGame().getLong(Rule.PENALTIES_DURATION);
long otherMaximum = otherClock.getMaximumTime();
long otherTime = otherClock.getTime();
long otherElapsed2 = otherMaximum - otherTime;
long otherNewTime = 0;
long thisNewTime = 0;
if(otherMaximum == penaltyDuration) {
// Simple, common case.
// Sitting jammer is released,
// and arriving jammer serves as much as sitting jammer did.
thisNewTime = otherElapsed2;
otherNewTime = 0;
} else if(otherMaximum > penaltyDuration) {
if(otherTime > penaltyDuration) {
// In this case sitting jammer has multiple penalties.
// Turn new jammer away,
// and reduce sitting jammer by one penalty.
// If sitting jammer happens to have even more
// than two penalties, other jammer can come and go
// reducing sitting jammer by one penalty each time
// until we arrive at the simple case.
thisNewTime = 1000; // to cause zero to appear
otherNewTime = otherTime - penaltyDuration;
} else {
// Reverts to simple case
// Sitting jammer leaves,
// New jammer sits for the amount that
// other jammer served of their second penalty.
thisNewTime = otherElapsed2 - penaltyDuration;
otherNewTime = 0;
}
} else {
// The nightmare scenario!
// A jammer has returned to the box while
// sitting jammer is already serving a reduced single penalty,
// so can't reduce that any more.
// Sitting jammer must finish their penalty,
// and arriving jammer gets regular penalty.
otherNewTime = otherTime;
thisNewTime = penaltyDuration;
}
pc.setMaximumTime(thisNewTime);
pc.setTime(thisNewTime);
pc.restart();
otherClock.setTime(otherNewTime);
}
break;
}
}
}
doJammerLogic(pc);
Fielding f = team.getPosition(getFloorPosition()).getCurrentFielding();
if( f != null ) {
BoxTrip bt = f.getCurrentBoxTrip();
Expand All @@ -201,7 +234,7 @@ public void resetBox() {
long penaltyDuration = team.getGame().getLong(Rule.PENALTIES_DURATION);
pc.setMaximumTime(penaltyDuration);
}
wallStartTime = 0;
setWallStartTime(0);
resetSkater();
}

Expand Down Expand Up @@ -256,10 +289,11 @@ public void endBox() {
s.setPenaltyCount(2, penaltyCountP2 + getCurNumPenalties());
}
}
//curTrip.end();
fpValid = false;
numPenalties = 1;
f.execute(Fielding.END_BOX_TRIP, Source.OTHER);
if(f.getCurrentBoxTrip() != null) {
f.getCurrentBoxTrip().end();
}
}
}
}
Expand Down Expand Up @@ -291,7 +325,9 @@ public void setBoxSkater(String number) {
if(cur_s != null) {
Fielding cur_f = cur_s.getCurrentFielding();
if(cur_f != null) {
cur_f.getCurrentBoxTrip().delete();
if(cur_f.getCurrentBoxTrip() != null) {
cur_f.getCurrentBoxTrip().delete();
}
}
}
}
Expand Down Expand Up @@ -368,6 +404,7 @@ public FloorPosition getFloorPosition() {

private void setWallStartTime(long w) {
wallStartTime = w;
set(STARTED, wallStartTime != 0);
}

private long getWallStartTime() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,6 @@ public void execute(Command prop, Source source) {
if (prop == UNEND_BOX_TRIP && getCurrentBoxTrip() != null && !getCurrentBoxTrip().isCurrent()) {
getCurrentBoxTrip().unend();
}
if (prop == END_BOX_TRIP && getCurrentBoxTrip() != null) {
getCurrentBoxTrip().end();
itemRemoved(BOX_TRIP, getCurrentBoxTrip(), source);
}
}

@Override
Expand Down
Loading

0 comments on commit e144773

Please sign in to comment.