diff --git a/apps/score/ChangeLog b/apps/score/ChangeLog index c61954d99..2f00260c0 100644 --- a/apps/score/ChangeLog +++ b/apps/score/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Minor code improvements 0.03: Bug fixes and some usability and performance improvements +0.04: Add squash preset, simplify logic, fix compatibility with BangleJS 2 diff --git a/apps/score/README.md b/apps/score/README.md index 6a5bf8172..f22b4ff22 100644 --- a/apps/score/README.md +++ b/apps/score/README.md @@ -31,8 +31,7 @@ In this mode any score increments will be decrements. To move back a set, reduce | `Sets per page` | How many sets should be shown in the app. Further sets will be available by scrolling (ignored if higher than `Sets to win`) | | `Score to win` | What score ends a given set | | `2-point lead` | Does winning a set require a two-point lead | -| `Maximum score?` | Should there be a maximum score, at which point the two-point lead rule falls away | -| `Maximum score` | At which score should the two-point lead rule fall away (ignored if lower than Sets to win) | +| `Maximum score` | Should there be a maximum score, at which point the two-point lead rule falls away (ignored if lower than Sets to win) | | `Tennis scoring` | If enabled, each point in a set will require a full tennis game | | `TB sets?` | Should sets that have reached `(maxScore-1):(maxScore-1)` be decided with a tiebreak | | All other options starting with TB | Equivalent to option with same name but applied to tiebreaks | diff --git a/apps/score/metadata.json b/apps/score/metadata.json index 083769617..f88003a6b 100644 --- a/apps/score/metadata.json +++ b/apps/score/metadata.json @@ -1,7 +1,7 @@ { "id": "score", "name": "Score Tracker", - "version": "0.03", + "version": "0.04", "description": "Score Tracker for sports that use plain numbers (e.g. Badminton, Volleyball, Soccer, Table Tennis, ...). Also supports tennis scoring.", "icon": "score.app.png", "screenshots": [{"url":"screenshot_score.png"}], diff --git a/apps/score/score.app.js b/apps/score/score.app.js index 271ca7e7f..b162d78ce 100644 --- a/apps/score/score.app.js +++ b/apps/score/score.app.js @@ -2,6 +2,12 @@ require('Font5x9Numeric7Seg').add(Graphics); require('Font7x11Numeric7Seg').add(Graphics); require('FontTeletext5x9Ascii').add(Graphics); +const KEY_SCORE_L = 0; +const KEY_SCORE_R = 1; +const KEY_MENU = 2; +const KEY_TENNIS_H = 3; +const KEY_TENNIS_L = 4; + let settingsMenu = eval(require('Storage').read('score.settings.js')); let settings = settingsMenu(null, null, true); @@ -44,45 +50,55 @@ function setupDisplay() { } function setupInputWatchers(init) { - Bangle.setUI('updown', v => { - if (v) { - if (isBangle1) { + Bangle.setUI('updown', + isBangle1 + ? (v => { + if (v) { let i = settings.mirrorScoreButtons ? v : v * -1; handleInput(Math.floor((i+2)/2)); - } else { + } + }) + : (v => { + if (v) { + // +1 -> 4 + // -1 -> 3 handleInput(Math.floor((v+2)/2)+3); } - } - }); + }) + ); if (init) { - if (isBangle1) { - setWatch(() => handleInput(2), BTN2, { repeat: true }); - } - Bangle.on('touch', (b, e) => { - if (isBangle1) { + setWatch( + () => handleInput(KEY_MENU), + isBangle1 ? BTN2 : BTN, + { repeat: true }, + ); + Bangle.on('touch', + isBangle1 + ? ((b, e) => { if (b === 1) { - handleInput(3); + handleInput(KEY_TENNIS_H); } else { - handleInput(4); + handleInput(KEY_TENNIS_L); } - } else { + }) + : ((b, e) => { if (e.y > 18) { if (e.x < getXCoord(w => w/2)) { - handleInput(0); + handleInput(KEY_SCORE_L); } else { - handleInput(1); + handleInput(KEY_SCORE_R); } } else { - // long press except if we have the menu opened or we are in the emulator (that doesn't + // long press except if we have the menu opened or we are in the emulator (that doesn't // seem to support long press events) if (e.type === 2 || settingsMenuOpened || process.env.BOARD === 'EMSCRIPTEN2') { - handleInput(2); + handleInput(KEY_MENU); } else { let p = null; - + if (matchWon(0)) p = 0; else if (matchWon(1)) p = 1; - + // display full instructions if there is space available, or brief ones otherwise if (p === null) { drawInitialMsg(); @@ -97,8 +113,8 @@ function setupInputWatchers(init) { } } } - } - }); + }) + ); } } @@ -137,14 +153,13 @@ function showSettingsMenu() { if (reset) { setupMatch(); } - if (isBangle1 || (!isBangle1 && back)) { - settingsMenuOpened = null; - draw(); + settingsMenuOpened = null; - setupDisplay(); - setupInputWatchers(); - } + draw(); + + setupDisplay(); + setupInputWatchers(); }, function (msg) { switch (msg) { case 'end_set': @@ -213,8 +228,8 @@ function tiebreakWon(set, player) { let p2Score = scores[set][3+~~!player]; // reachedMaxScore || (winScoreReached && isTwoAhead); - return (settings.maxScoreTiebreakEnableMaxScore && pScore >= tiebreakMaxScore()) || - ((pScore >= settings.maxScoreTiebreakWinScore) && + return (settings.maxScoreTiebreakEnableMaxScore && pScore >= tiebreakMaxScore()) || + ((pScore >= settings.maxScoreTiebreakWinScore) && (!settings.maxScoreTiebreakEnableTwoAhead || pScore - p2Score >= 2)); } @@ -224,8 +239,8 @@ function setWon(set, player) { // (tiebreak won / max score) || (winScoreReached && isTwoAhead) || manuallyEndedWon return ( - (settings.enableMaxScoreTiebreak ? tiebreakWon(set, player) : settings.enableMaxScore && pScore >= maxScore()) || - (pScore >= settings.winScore && (!settings.enableTwoAhead || pScore - p2Score >= 2)) || + (settings.enableMaxScoreTiebreak ? tiebreakWon(set, player) : settings.enableMaxScore && pScore >= maxScore()) || + (pScore >= settings.winScore && (!settings.enableTwoAhead || pScore - p2Score >= 2)) || (cSet > set ? pScore > p2Score : false) ); } @@ -344,7 +359,7 @@ function handleInput(button) { // console.log('button:', button); if (settingsMenuOpened) { - if (!isBangle1 && button == 2) { + if (!isBangle1 && button == KEY_MENU) { // Bangle2 long press, hide menu E.showMenu(); settingsMenuOpened = null; @@ -353,21 +368,21 @@ function handleInput(button) { setupDisplay(); setupInputWatchers(); - + } return; } switch (button) { - case 0: - case 1: + case KEY_SCORE_L: + case KEY_SCORE_R: score(button); break; - case 2: + case KEY_MENU: showSettingsMenu(); return; - case 3: - case 4: { + case KEY_TENNIS_H: + case KEY_TENNIS_L: { let hLimit = currentSet() - setsPerPage() + 1; let lLimit = 0; let val = (button * 2 - 7); @@ -382,8 +397,7 @@ function handleInput(button) { } function draw() { - g.setFontAlign(0,0); - g.clear(); + g.reset().setFontAlign(0,0).clear(); for (let p = 0; p < 2; p++) { if (matchWon(p)) { @@ -538,4 +552,4 @@ setupDisplay(); setupInputWatchers(true); setupMatch(); draw(); -drawInitialMsg(); \ No newline at end of file +drawInitialMsg(); diff --git a/apps/score/score.presets.json b/apps/score/score.presets.json index b57b52157..c11d7cebd 100644 --- a/apps/score/score.presets.json +++ b/apps/score/score.presets.json @@ -26,5 +26,10 @@ "winScore": 11, "enableTwoAhead": true, "enableMaxScore": false + }, + "Squash": { + "winScore": 9, + "enableTwoAhead": true, + "enableMaxScore": false } } diff --git a/apps/score/score.settings.js b/apps/score/score.settings.js index c34c063a5..d5da0dd27 100644 --- a/apps/score/score.settings.js +++ b/apps/score/score.settings.js @@ -1,220 +1,194 @@ -(function () { - return (function (back, inApp, ret) { - const isBangle1 = process.env.BOARD === 'BANGLEJS' +(function (back, inApp, ret) { + const isBangle1 = process.env.BOARD === 'BANGLEJS' - function fillSettingsWithDefaults(settings) { - if (isBangle1) { - if (settings.mirrorScoreButtons == null) { - settings.mirrorScoreButtons = false; - } - if (settings.keepDisplayOn == null) { - settings.keepDisplayOn = true; - } - } - if (settings.winSets == null) { - settings.winSets = 2; - } - if (settings.setsPerPage == null) { - settings.setsPerPage = 5; - } - if (settings.winScore == null) { - settings.winScore = 21; - } - if (settings.enableTwoAhead == null) { - settings.enableTwoAhead = true; - } - if (settings.enableMaxScore == null) { - settings.enableMaxScore = true; - } - if (settings.maxScore == null) { - settings.maxScore = 30; - } - if (settings.enableTennisScoring == null) { - settings.enableTennisScoring = false; - } + function fillSettingsWithDefaults(settings) { + settings = Object.assign({ + winSets: 2, + setsPerPage: 5, + winScore: 21, + enableTwoAhead: true, + enableMaxScore: true, + maxScore: 30, + enableTennisScoring: false, - if (settings.enableMaxScoreTiebreak == null) { - settings.enableMaxScoreTiebreak = false; - } - if (settings.maxScoreTiebreakWinScore == null) { - settings.maxScoreTiebreakWinScore = 6; - } - if (settings.maxScoreTiebreakEnableTwoAhead == null) { - settings.maxScoreTiebreakEnableTwoAhead = true; - } - if (settings.maxScoreTiebreakEnableMaxScore == null) { - settings.maxScoreTiebreakEnableMaxScore = false; - } - if (settings.maxScoreTiebreakMaxScore == null) { - settings.maxScoreTiebreakMaxScore = 15; - } + enableMaxScoreTiebreak: false, + maxScoreTiebreakWinScore: 6, + maxScoreTiebreakEnableTwoAhead: true, + maxScoreTiebreakEnableMaxScore: false, + maxScoreTiebreakMaxScore: 15, + }, settings); - return settings; + if (isBangle1) { + settings = Object.assign({ + mirrorScoreButtons: false, + keepDisplayOn: true, + }, settings); } - const fileName = 'score.json'; - let settings = require('Storage').readJSON(fileName, 1) || {}; - const offon = ['No', 'Yes']; + return settings; + } - let presetsFileName = 'score.presets.json'; - let presets = require('Storage').readJSON(presetsFileName); - let presetNames = Object.keys(presets); + const fileName = 'score.json'; + let settings = require('Storage').readJSON(fileName, 1) || {}; + const offon = ['No', 'Yes']; - let changed = false; + let presetsFileName = 'score.presets.json'; + let presets = require('Storage').readJSON(presetsFileName); + let presetNames = Object.keys(presets); - function save(settings) { - require('Storage').writeJSON(fileName, settings); + let changed = false; + + function save(settings) { + require('Storage').writeJSON(fileName, settings); + } + + function setAndSave(key, value, notChanged) { + if (!notChanged) { + changed = true; } - - function setAndSave(key, value, notChanged) { - if (!notChanged) { - changed = true; - } - settings[key] = value; - if (key === 'winScore' && settings.maxScore < value) { - settings.maxScore = value; - } - save(settings); + settings[key] = value; + if (key === 'winScore' && settings.maxScore < value) { + settings.maxScore = value; } + save(settings); + } - settings = fillSettingsWithDefaults(settings); + settings = fillSettingsWithDefaults(settings); - if (ret) { - return settings; - } + if (ret) { + return settings; + } - const presetMenu = function (appMenuBack) { - let ret = function (changed) { E.showMenu(appMenu(appMenuBack, changed ? 2 : null)); }; - let m = { - '': {'title': 'Score Presets'}, - '< Back': ret, - }; - for (let i = 0; i < presetNames.length; i++) { - m[presetNames[i]] = (function (i) { - return function() { - changed = true; - let mirrorScoreButtons = settings.mirrorScoreButtons; - let keepDisplayOn = settings.keepDisplayOn; - - settings = fillSettingsWithDefaults(presets[presetNames[i]]); - - settings.mirrorScoreButtons = mirrorScoreButtons; - settings.keepDisplayOn = keepDisplayOn; - save(settings); - ret(true); - }; - })(i); - } - - return m; + const presetMenu = function (appMenuBack) { + let ret = function (changed) { E.showMenu(appMenu(appMenuBack, changed ? 2 : null)); }; + let m = { + '': {'title': 'Score Presets'}, + '< Back': ret, }; + for (let i = 0; i < presetNames.length; i++) { + m[presetNames[i]] = (function (i) { + return function() { + changed = true; + let mirrorScoreButtons = settings.mirrorScoreButtons; + let keepDisplayOn = settings.keepDisplayOn; - const appMenu = function (back, selected) { - let m = {}; + settings = fillSettingsWithDefaults(presets[presetNames[i]]); - m[''] = {'title': 'Score Settings'}; - if (selected != null) { - m[''].selected = selected; - } - m['< Back'] = function () { back(settings, changed, true); }; - m['Presets'] = function () { E.showMenu(presetMenu(back)); }; - if (isBangle1) { - m['Mirror Buttons'] = { - value: settings.mirrorScoreButtons, - format: m => offon[~~m], - onchange: m => setAndSave('mirrorScoreButtons', m, true), + settings.mirrorScoreButtons = mirrorScoreButtons; + settings.keepDisplayOn = keepDisplayOn; + save(settings); + ret(true); }; - m['Keep display on'] = { - value: settings.keepDisplayOn, - format: m => offon[~~m], - onchange: m => setAndSave('keepDisplayOn', m, true), - } - } - m['Sets to win'] = { - value: settings.winSets, - min:1, - onchange: m => setAndSave('winSets', m), - }; - m['Sets per page'] = { - value: settings.setsPerPage, - min:1, - max:5, - onchange: m => setAndSave('setsPerPage', m), - }; - m['Score to win'] = { - value: settings.winScore, - min:1, - max: 999, - onchange: m => setAndSave('winScore', m), - }; - m['2-point lead'] = { - value: settings.enableTwoAhead, - format: m => offon[~~m], - onchange: m => setAndSave('enableTwoAhead', m), - }; - m['Maximum score?'] = { - value: settings.enableMaxScore, - format: m => offon[~~m], - onchange: m => setAndSave('enableMaxScore', m), - }; - m['Maximum score'] = { - value: settings.maxScore, - min: 1, - max: 999, - onchange: m => setAndSave('maxScore', m), - }; - m['Tennis scoring'] = { - value: settings.enableTennisScoring, - format: m => offon[~~m], - onchange: m => setAndSave('enableTennisScoring', m), - }; - m['TB sets?'] = { - value: settings.enableMaxScoreTiebreak, - format: m => offon[~~m], - onchange: m => setAndSave('enableMaxScoreTiebreak', m), - }; - m['TB Score to win'] = { - value: settings.maxScoreTiebreakWinScore, - onchange: m => setAndSave('maxScoreTiebreakWinScore', m), - }; - m['TB 2-point lead'] = { - value: settings.maxScoreTiebreakEnableTwoAhead, - format: m => offon[~~m], - onchange: m => setAndSave('maxScoreTiebreakEnableTwoAhead', m), - }; - m['TB max score?'] = { - value: settings.maxScoreTiebreakEnableMaxScore, - format: m => offon[~~m], - onchange: m => setAndSave('maxScoreTiebreakEnableMaxScore', m), - }; - m['TB max score'] = { - value: settings.maxScoreTiebreakMaxScore, - onchange: m => setAndSave('maxScoreTiebreakMaxScore', m), - }; - - return m; - }; - - const inAppMenu = function () { - let m = { - '': {'title': 'Score Menu'}, - '< Back': function () { back(settings, changed, false); }, - 'Correct mode': function () { inApp('correct_mode'); back(settings, false, true); }, - 'Reset match': function () { back(settings, true, true); }, - 'End current set': function () { inApp('end_set'); back(settings, changed, true); }, - 'Configuration': function () { E.showMenu(appMenu(function () { - E.showMenu(inAppMenu()); - })); }, - }; - - return m; - }; - - if (inApp != null) { - E.showMenu(inAppMenu()); - } else { - E.showMenu(appMenu(back)); + })(i); } - }); -})() + return m; + }; + + const appMenu = function (back, selected) { + let m = {}; + + m[''] = {'title': 'Score Settings'}; + if (selected != null) { + m[''].selected = selected; + } + m['< Back'] = function () { back(settings, changed, true); }; + m['Presets'] = function () { E.showMenu(presetMenu(back)); }; + if (isBangle1) { + m['Mirror Buttons'] = { + value: settings.mirrorScoreButtons, + format: m => offon[~~m], + onchange: m => setAndSave('mirrorScoreButtons', m, true), + }; + m['Keep display on'] = { + value: settings.keepDisplayOn, + format: m => offon[~~m], + onchange: m => setAndSave('keepDisplayOn', m, true), + } + } + m['Sets to win'] = { + value: settings.winSets, + min:1, + onchange: m => setAndSave('winSets', m), + }; + m['Sets per page'] = { + value: settings.setsPerPage, + min:1, + max:5, + onchange: m => setAndSave('setsPerPage', m), + }; + m['Score to win'] = { + value: settings.winScore, + min:1, + max: 999, + onchange: m => setAndSave('winScore', m), + }; + m['2-point lead'] = { + value: settings.enableTwoAhead, + format: m => offon[~~m], + onchange: m => setAndSave('enableTwoAhead', m), + }; + m['Maximum score?'] = { + value: settings.enableMaxScore, + format: m => offon[~~m], + onchange: m => setAndSave('enableMaxScore', m), + }; + m['Maximum score'] = { + value: settings.maxScore, + min: 1, + max: 999, + onchange: m => setAndSave('maxScore', m), + }; + m['Tennis scoring'] = { + value: settings.enableTennisScoring, + format: m => offon[~~m], + onchange: m => setAndSave('enableTennisScoring', m), + }; + m['TB sets?'] = { + value: settings.enableMaxScoreTiebreak, + format: m => offon[~~m], + onchange: m => setAndSave('enableMaxScoreTiebreak', m), + }; + m['TB Score to win'] = { + value: settings.maxScoreTiebreakWinScore, + onchange: m => setAndSave('maxScoreTiebreakWinScore', m), + }; + m['TB 2-point lead'] = { + value: settings.maxScoreTiebreakEnableTwoAhead, + format: m => offon[~~m], + onchange: m => setAndSave('maxScoreTiebreakEnableTwoAhead', m), + }; + m['TB max score?'] = { + value: settings.maxScoreTiebreakEnableMaxScore, + format: m => offon[~~m], + onchange: m => setAndSave('maxScoreTiebreakEnableMaxScore', m), + }; + m['TB max score'] = { + value: settings.maxScoreTiebreakMaxScore, + onchange: m => setAndSave('maxScoreTiebreakMaxScore', m), + }; + + return m; + }; + + const inAppMenu = function () { + let m = { + '': {'title': 'Score Menu'}, + '< Back': function () { back(settings, changed); }, + 'Correct mode': function () { inApp('correct_mode'); back(settings, false); }, + 'Reset match': function () { back(settings, true); }, + 'End current set': function () { inApp('end_set'); back(settings, changed); }, + 'Configuration': function () { E.showMenu(appMenu(function () { + E.showMenu(inAppMenu()); + })); }, + }; + + return m; + }; + + if (inApp != null) { + E.showMenu(inAppMenu()); + } else { + E.showMenu(appMenu(back)); + } +})