ramanaprabhusana commited on
Commit
2e21060
·
verified ·
1 Parent(s): 16a19cb

Polish RunRate Lab cricket bat and ball visuals

Browse files
Files changed (1) hide show
  1. app.py +136 -40
app.py CHANGED
@@ -738,9 +738,10 @@ GAME_HTML = r"""
738
  position: absolute;
739
  z-index: 6;
740
  right: 16px;
741
- bottom: 16px;
742
- width: min(380px, calc(100% - 32px));
743
- max-height: min(460px, calc(100% - 96px));
 
744
  overflow: auto;
745
  border: 1px solid var(--line);
746
  border-radius: 12px;
@@ -1080,6 +1081,7 @@ GAME_HTML = r"""
1080
  .coach-panel {
1081
  left: 14px;
1082
  right: 14px;
 
1083
  bottom: 14px;
1084
  width: auto;
1085
  max-height: 328px;
@@ -1327,6 +1329,7 @@ GAME_HTML = r"""
1327
  log: [],
1328
  particles: [],
1329
  trail: [],
 
1330
  resultFlash: 0,
1331
  wicketShake: 0,
1332
  processStreak: 0,
@@ -1508,6 +1511,7 @@ GAME_HTML = r"""
1508
  state.deliveryLine = rand(-0.28, 0.28);
1509
  state.ballProgress = 0;
1510
  state.trail = [];
 
1511
  state.message = `${pitch.Delivery}. Hold to charge. Release at impact.`;
1512
  }
1513
 
@@ -1519,6 +1523,7 @@ GAME_HTML = r"""
1519
  state.log = [];
1520
  state.particles = [];
1521
  state.trail = [];
 
1522
  state.lastInsight = null;
1523
  state.processSummary = null;
1524
  state.processStreak = 0;
@@ -1621,7 +1626,9 @@ GAME_HTML = r"""
1621
  state.message = "Review the analytics coach, then press Next Ball.";
1622
  statusHint.textContent = "Review state. Press Next Ball or Enter when ready.";
1623
 
 
1624
  if (!sampled.wicket && sampled.runs > 0) addShotTrail(sampled.runs);
 
1625
  addImpactParticles(sampled.wicket ? fxColor("risk") : sampled.runs >= 4 ? fxColor("boundary") : fxColor("single"));
1626
  if (sampled.runs >= 4) state.resultFlash = state.settings.reduceFlash ? 0.35 : 1;
1627
  if (sampled.wicket) state.wicketShake = 1;
@@ -1649,6 +1656,7 @@ GAME_HTML = r"""
1649
  state.ballProgress = 0;
1650
  state.swingState = "ready";
1651
  state.trail = [];
 
1652
  nextBallBtn.classList.add("hidden");
1653
  detailsBtn.classList.add("hidden");
1654
  state.detailsOpen = false;
@@ -1972,6 +1980,7 @@ GAME_HTML = r"""
1972
 
1973
  function drawBallPath() {
1974
  if (!state.delivery) return;
 
1975
  const start = pitchPoint(0.08, state.deliveryLine);
1976
  const end = pitchPoint(0.88, state.deliveryLine * 0.25);
1977
  ctx.save();
@@ -1990,23 +1999,59 @@ GAME_HTML = r"""
1990
  ctx.restore();
1991
  }
1992
 
1993
- function drawBall() {
1994
- if (state.mode !== "bowling" && state.mode !== "between") return;
1995
- const p = ballPosition();
1996
- if (state.mode === "between") return;
1997
  ctx.save();
1998
- ctx.fillStyle = "#ef4444";
 
1999
  ctx.beginPath();
2000
- ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
2001
  ctx.fill();
2002
- ctx.strokeStyle = "rgba(255,255,255,0.55)";
2003
- ctx.lineWidth = Math.max(1, p.r * 0.18);
 
 
 
 
 
 
 
 
 
 
 
 
 
2004
  ctx.beginPath();
2005
- ctx.arc(p.x - p.r * 0.1, p.y - p.r * 0.1, p.r * 0.55, -0.8, 0.8);
2006
  ctx.stroke();
 
 
 
 
 
 
 
 
2007
  ctx.restore();
2008
  }
2009
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2010
  function drawShotTrail() {
2011
  state.trail.forEach((trail) => {
2012
  trail.life -= 0.018;
@@ -2024,21 +2069,65 @@ GAME_HTML = r"""
2024
  state.trail = state.trail.filter((trail) => trail.life > 0);
2025
  }
2026
 
2027
- function addShotTrail(runs) {
2028
  const aim = rowBy(aims, "Aim", state.selectedAim);
2029
  const angle = (Number(aim.Angle) - 90 + (state.selectedShot === "Cut / Pull" ? 14 : 0)) * Math.PI / 180;
2030
  const start = pitchPoint(0.89, 0);
2031
  const length = runs >= 6 ? height * 0.60 : runs === 4 ? height * 0.48 : height * 0.26;
2032
- state.trail.push({
2033
  x0: start.x,
2034
  y0: start.y,
2035
  cx: start.x + Math.cos(angle) * length * 0.38,
2036
  cy: start.y + Math.sin(angle) * length * 0.36,
2037
  x1: start.x + Math.cos(angle) * length,
2038
  y1: start.y + Math.sin(angle) * length,
2039
- runs,
 
 
 
 
 
 
 
2040
  life: 1
2041
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2042
  }
2043
 
2044
  function addImpactParticles(color) {
@@ -2105,43 +2194,41 @@ GAME_HTML = r"""
2105
  const handleW = 7 * scale;
2106
  const bladeTopW = 23 * scale;
2107
  const bladeBotW = 42 * scale;
2108
- const dx = Math.cos(angle);
2109
- const dy = Math.sin(angle);
2110
- const nx = -dy;
2111
- const ny = dx;
2112
- const hEnd = { x: anchor.x + dx * handleLen, y: anchor.y + dy * handleLen };
2113
- const bEnd = { x: hEnd.x + dx * bladeLen, y: hEnd.y + dy * bladeLen };
2114
 
2115
  ctx.save();
 
 
 
2116
  ctx.strokeStyle = "#4a3417";
2117
  ctx.lineWidth = handleW;
2118
  ctx.lineCap = "round";
2119
  ctx.beginPath();
2120
- ctx.moveTo(anchor.x, anchor.y);
2121
- ctx.lineTo(hEnd.x, hEnd.y);
2122
  ctx.stroke();
2123
 
2124
  ctx.strokeStyle = "#f8fafc";
2125
  ctx.lineWidth = 2 * scale;
2126
  for (let i = 0; i < 3; i += 1) {
2127
- const gx = anchor.x + dx * (12 + i * 12) * scale;
2128
- const gy = anchor.y + dy * (12 + i * 12) * scale;
2129
  ctx.beginPath();
2130
- ctx.moveTo(gx + nx * 6 * scale, gy + ny * 6 * scale);
2131
- ctx.lineTo(gx - nx * 6 * scale, gy - ny * 6 * scale);
2132
  ctx.stroke();
2133
  }
2134
 
2135
- const shoulder = { x: hEnd.x + dx * 12 * scale, y: hEnd.y + dy * 12 * scale };
 
2136
  ctx.fillStyle = "#d8ae55";
2137
  ctx.strokeStyle = "#76551e";
2138
  ctx.lineWidth = 3 * scale;
2139
  ctx.beginPath();
2140
- ctx.moveTo(hEnd.x + nx * bladeTopW * 0.5, hEnd.y + ny * bladeTopW * 0.5);
2141
- ctx.lineTo(shoulder.x - nx * bladeTopW * 0.5, shoulder.y - ny * bladeTopW * 0.5);
2142
- ctx.lineTo(bEnd.x - nx * bladeBotW * 0.5, bEnd.y - ny * bladeBotW * 0.5);
2143
- ctx.quadraticCurveTo(bEnd.x, bEnd.y + 10 * scale, bEnd.x + nx * bladeBotW * 0.5, bEnd.y + ny * bladeBotW * 0.5);
2144
- ctx.lineTo(shoulder.x + nx * bladeTopW * 0.5, shoulder.y + ny * bladeTopW * 0.5);
 
2145
  ctx.closePath();
2146
  ctx.fill();
2147
  ctx.stroke();
@@ -2149,8 +2236,15 @@ GAME_HTML = r"""
2149
  ctx.strokeStyle = "rgba(255,255,255,0.32)";
2150
  ctx.lineWidth = 2 * scale;
2151
  ctx.beginPath();
2152
- ctx.moveTo(shoulder.x + nx * 4 * scale, shoulder.y + ny * 4 * scale);
2153
- ctx.lineTo(bEnd.x + nx * 10 * scale, bEnd.y + ny * 10 * scale);
 
 
 
 
 
 
 
2154
  ctx.stroke();
2155
  ctx.restore();
2156
  }
@@ -2162,16 +2256,17 @@ GAME_HTML = r"""
2162
  const x = base.x + shake;
2163
  const y = base.y + 18 * scale;
2164
  const progress = clamp((now - state.swingStart) / 420, 0, 1);
 
2165
  let batAngle = -1.25;
2166
  let shoulderTilt = 0;
2167
  if (state.mode === "bowling" && state.charging) {
2168
  batAngle = -1.54 - clamp(state.power / 100, 0, 1) * 0.42;
2169
  shoulderTilt = -0.10;
2170
  } else if (state.swingState === "contact") {
2171
- batAngle = -1.70 + progress * 2.45;
2172
- shoulderTilt = 0.20 * progress;
2173
  } else if (state.swingState === "miss") {
2174
- batAngle = -1.60 + progress * 1.78;
2175
  shoulderTilt = 0.10;
2176
  } else {
2177
  batAngle = -1.24;
@@ -2212,6 +2307,8 @@ GAME_HTML = r"""
2212
  drawLimb(x + 48 * scale, shoulderY + 30 * scale, leftGlove.x, leftGlove.y, 15 * scale, "#f8fafc");
2213
  drawLimb(leftGlove.x, leftGlove.y, rightGlove.x, rightGlove.y, 15 * scale, "#f8fafc");
2214
 
 
 
2215
  ctx.fillStyle = "#facc15";
2216
  ctx.beginPath();
2217
  ctx.arc(leftGlove.x, leftGlove.y, 13 * scale, 0, Math.PI * 2);
@@ -2220,8 +2317,6 @@ GAME_HTML = r"""
2220
  ctx.arc(rightGlove.x, rightGlove.y, 13 * scale, 0, Math.PI * 2);
2221
  ctx.fill();
2222
 
2223
- drawBat(leftGlove, rightGlove, batAngle, scale);
2224
-
2225
  ctx.fillStyle = "#f8fafc";
2226
  ctx.beginPath();
2227
  ctx.arc(x, headY, 32 * scale, 0, Math.PI * 2);
@@ -2270,6 +2365,7 @@ GAME_HTML = r"""
2270
  drawParticles();
2271
  drawStumps(width / 2, pitchPoint(0.89, 0).y + 26, 1.15);
2272
  drawBatter(now);
 
2273
  drawScoreState();
2274
  drawFlash();
2275
  }
 
738
  position: absolute;
739
  z-index: 6;
740
  right: 16px;
741
+ top: 128px;
742
+ bottom: auto;
743
+ width: min(360px, calc(100% - 32px));
744
+ max-height: min(430px, calc(100% - 154px));
745
  overflow: auto;
746
  border: 1px solid var(--line);
747
  border-radius: 12px;
 
1081
  .coach-panel {
1082
  left: 14px;
1083
  right: 14px;
1084
+ top: auto;
1085
  bottom: 14px;
1086
  width: auto;
1087
  max-height: 328px;
 
1329
  log: [],
1330
  particles: [],
1331
  trail: [],
1332
+ lastBallVisual: null,
1333
  resultFlash: 0,
1334
  wicketShake: 0,
1335
  processStreak: 0,
 
1511
  state.deliveryLine = rand(-0.28, 0.28);
1512
  state.ballProgress = 0;
1513
  state.trail = [];
1514
+ state.lastBallVisual = null;
1515
  state.message = `${pitch.Delivery}. Hold to charge. Release at impact.`;
1516
  }
1517
 
 
1523
  state.log = [];
1524
  state.particles = [];
1525
  state.trail = [];
1526
+ state.lastBallVisual = null;
1527
  state.lastInsight = null;
1528
  state.processSummary = null;
1529
  state.processStreak = 0;
 
1626
  state.message = "Review the analytics coach, then press Next Ball.";
1627
  statusHint.textContent = "Review state. Press Next Ball or Enter when ready.";
1628
 
1629
+ if (sampled.wicket) addWicketBallVisual();
1630
  if (!sampled.wicket && sampled.runs > 0) addShotTrail(sampled.runs);
1631
+ if (!sampled.wicket && sampled.runs === 0) addDotBallVisual();
1632
  addImpactParticles(sampled.wicket ? fxColor("risk") : sampled.runs >= 4 ? fxColor("boundary") : fxColor("single"));
1633
  if (sampled.runs >= 4) state.resultFlash = state.settings.reduceFlash ? 0.35 : 1;
1634
  if (sampled.wicket) state.wicketShake = 1;
 
1656
  state.ballProgress = 0;
1657
  state.swingState = "ready";
1658
  state.trail = [];
1659
+ state.lastBallVisual = null;
1660
  nextBallBtn.classList.add("hidden");
1661
  detailsBtn.classList.add("hidden");
1662
  state.detailsOpen = false;
 
1980
 
1981
  function drawBallPath() {
1982
  if (!state.delivery) return;
1983
+ if (state.mode === "between" || state.mode === "gameover") return;
1984
  const start = pitchPoint(0.08, state.deliveryLine);
1985
  const end = pitchPoint(0.88, state.deliveryLine * 0.25);
1986
  ctx.save();
 
1999
  ctx.restore();
2000
  }
2001
 
2002
+ function drawCricketBall(x, y, r, rotation = 0, alpha = 1) {
 
 
 
2003
  ctx.save();
2004
+ ctx.globalAlpha = alpha;
2005
+ ctx.fillStyle = "rgba(0,0,0,0.26)";
2006
  ctx.beginPath();
2007
+ ctx.ellipse(x + r * 0.18, y + r * 0.78, r * 0.92, r * 0.30, 0, 0, Math.PI * 2);
2008
  ctx.fill();
2009
+
2010
+ const gradient = ctx.createRadialGradient(x - r * 0.35, y - r * 0.35, r * 0.1, x, y, r);
2011
+ gradient.addColorStop(0, "#ff7a7a");
2012
+ gradient.addColorStop(0.58, "#ef4444");
2013
+ gradient.addColorStop(1, "#8f1f1f");
2014
+ ctx.fillStyle = gradient;
2015
+ ctx.beginPath();
2016
+ ctx.arc(x, y, r, 0, Math.PI * 2);
2017
+ ctx.fill();
2018
+
2019
+ ctx.translate(x, y);
2020
+ ctx.rotate(rotation);
2021
+ ctx.strokeStyle = "rgba(255,255,255,0.72)";
2022
+ ctx.lineWidth = Math.max(1.2, r * 0.15);
2023
+ ctx.lineCap = "round";
2024
  ctx.beginPath();
2025
+ ctx.arc(-r * 0.18, -r * 0.02, r * 0.62, -0.95, 0.95);
2026
  ctx.stroke();
2027
+ ctx.strokeStyle = "rgba(255,255,255,0.42)";
2028
+ ctx.lineWidth = Math.max(1, r * 0.08);
2029
+ for (let i = -2; i <= 2; i += 1) {
2030
+ ctx.beginPath();
2031
+ ctx.moveTo(-r * 0.24, i * r * 0.18);
2032
+ ctx.lineTo(-r * 0.08, i * r * 0.18 + r * 0.05);
2033
+ ctx.stroke();
2034
+ }
2035
  ctx.restore();
2036
  }
2037
 
2038
+ function drawBall() {
2039
+ if (state.mode !== "bowling") return;
2040
+ const p = ballPosition();
2041
+ drawCricketBall(p.x, p.y, p.r, p.t * Math.PI * 4, 1);
2042
+ }
2043
+
2044
+ function drawReviewBall(now) {
2045
+ if (state.mode !== "between" || !state.lastBallVisual) return;
2046
+ const visual = state.lastBallVisual;
2047
+ const elapsed = clamp((now - visual.createdAt) / 860, 0, 1);
2048
+ const ease = 1 - Math.pow(1 - elapsed, 2);
2049
+ const x = (1 - ease) * visual.x0 + ease * visual.x1;
2050
+ const curveY = (1 - ease) * (1 - ease) * visual.y0 + 2 * (1 - ease) * ease * visual.cy + ease * ease * visual.y1;
2051
+ const r = visual.wicket ? 9 : visual.runs >= 4 ? 8 : 7;
2052
+ drawCricketBall(x, curveY, r, now / 95, 0.94);
2053
+ }
2054
+
2055
  function drawShotTrail() {
2056
  state.trail.forEach((trail) => {
2057
  trail.life -= 0.018;
 
2069
  state.trail = state.trail.filter((trail) => trail.life > 0);
2070
  }
2071
 
2072
+ function shotPathFor(runs) {
2073
  const aim = rowBy(aims, "Aim", state.selectedAim);
2074
  const angle = (Number(aim.Angle) - 90 + (state.selectedShot === "Cut / Pull" ? 14 : 0)) * Math.PI / 180;
2075
  const start = pitchPoint(0.89, 0);
2076
  const length = runs >= 6 ? height * 0.60 : runs === 4 ? height * 0.48 : height * 0.26;
2077
+ return {
2078
  x0: start.x,
2079
  y0: start.y,
2080
  cx: start.x + Math.cos(angle) * length * 0.38,
2081
  cy: start.y + Math.sin(angle) * length * 0.36,
2082
  x1: start.x + Math.cos(angle) * length,
2083
  y1: start.y + Math.sin(angle) * length,
2084
+ runs
2085
+ };
2086
+ }
2087
+
2088
+ function addShotTrail(runs) {
2089
+ const path = shotPathFor(runs);
2090
+ state.trail.push({
2091
+ ...path,
2092
  life: 1
2093
  });
2094
+ state.lastBallVisual = {
2095
+ ...path,
2096
+ wicket: false,
2097
+ createdAt: performance.now()
2098
+ };
2099
+ }
2100
+
2101
+ function addWicketBallVisual() {
2102
+ const start = pitchPoint(0.89, 0);
2103
+ const side = state.deliveryLine < 0 ? -1 : 1;
2104
+ state.lastBallVisual = {
2105
+ x0: start.x + side * 10,
2106
+ y0: start.y - 16,
2107
+ cx: start.x + side * rand(26, 40),
2108
+ cy: start.y - rand(24, 42),
2109
+ x1: start.x + side * rand(48, 72),
2110
+ y1: start.y + rand(0, 12),
2111
+ runs: 0,
2112
+ wicket: true,
2113
+ createdAt: performance.now()
2114
+ };
2115
+ }
2116
+
2117
+ function addDotBallVisual() {
2118
+ const start = pitchPoint(0.89, state.deliveryLine * 0.15);
2119
+ const side = state.selectedAim === "Leg Side" ? -1 : 1;
2120
+ state.lastBallVisual = {
2121
+ x0: start.x + side * 12,
2122
+ y0: start.y - 14,
2123
+ cx: start.x + side * rand(26, 42),
2124
+ cy: start.y - rand(18, 34),
2125
+ x1: start.x + side * rand(52, 82),
2126
+ y1: start.y + rand(2, 16),
2127
+ runs: 0,
2128
+ wicket: false,
2129
+ createdAt: performance.now()
2130
+ };
2131
  }
2132
 
2133
  function addImpactParticles(color) {
 
2194
  const handleW = 7 * scale;
2195
  const bladeTopW = 23 * scale;
2196
  const bladeBotW = 42 * scale;
 
 
 
 
 
 
2197
 
2198
  ctx.save();
2199
+ ctx.translate(anchor.x, anchor.y);
2200
+ ctx.rotate(angle);
2201
+
2202
  ctx.strokeStyle = "#4a3417";
2203
  ctx.lineWidth = handleW;
2204
  ctx.lineCap = "round";
2205
  ctx.beginPath();
2206
+ ctx.moveTo(0, 0);
2207
+ ctx.lineTo(handleLen, 0);
2208
  ctx.stroke();
2209
 
2210
  ctx.strokeStyle = "#f8fafc";
2211
  ctx.lineWidth = 2 * scale;
2212
  for (let i = 0; i < 3; i += 1) {
2213
+ const gx = (13 + i * 12) * scale;
 
2214
  ctx.beginPath();
2215
+ ctx.moveTo(gx, -6 * scale);
2216
+ ctx.lineTo(gx, 6 * scale);
2217
  ctx.stroke();
2218
  }
2219
 
2220
+ const shoulder = handleLen + 10 * scale;
2221
+ const toe = handleLen + bladeLen;
2222
  ctx.fillStyle = "#d8ae55";
2223
  ctx.strokeStyle = "#76551e";
2224
  ctx.lineWidth = 3 * scale;
2225
  ctx.beginPath();
2226
+ ctx.moveTo(handleLen, -bladeTopW * 0.44);
2227
+ ctx.quadraticCurveTo(shoulder - 4 * scale, -bladeTopW * 0.56, shoulder, -bladeTopW * 0.5);
2228
+ ctx.lineTo(toe - 16 * scale, -bladeBotW * 0.5);
2229
+ ctx.quadraticCurveTo(toe + 9 * scale, 0, toe - 16 * scale, bladeBotW * 0.5);
2230
+ ctx.lineTo(shoulder, bladeTopW * 0.5);
2231
+ ctx.quadraticCurveTo(shoulder - 5 * scale, bladeTopW * 0.58, handleLen, bladeTopW * 0.44);
2232
  ctx.closePath();
2233
  ctx.fill();
2234
  ctx.stroke();
 
2236
  ctx.strokeStyle = "rgba(255,255,255,0.32)";
2237
  ctx.lineWidth = 2 * scale;
2238
  ctx.beginPath();
2239
+ ctx.moveTo(shoulder + 8 * scale, -4 * scale);
2240
+ ctx.lineTo(toe - 20 * scale, -11 * scale);
2241
+ ctx.stroke();
2242
+
2243
+ ctx.strokeStyle = "rgba(118,85,30,0.35)";
2244
+ ctx.lineWidth = 1.5 * scale;
2245
+ ctx.beginPath();
2246
+ ctx.moveTo(shoulder + 5 * scale, 0);
2247
+ ctx.lineTo(toe - 22 * scale, 0);
2248
  ctx.stroke();
2249
  ctx.restore();
2250
  }
 
2256
  const x = base.x + shake;
2257
  const y = base.y + 18 * scale;
2258
  const progress = clamp((now - state.swingStart) / 420, 0, 1);
2259
+ const finishProgress = state.mode === "between" ? 0.76 : progress;
2260
  let batAngle = -1.25;
2261
  let shoulderTilt = 0;
2262
  if (state.mode === "bowling" && state.charging) {
2263
  batAngle = -1.54 - clamp(state.power / 100, 0, 1) * 0.42;
2264
  shoulderTilt = -0.10;
2265
  } else if (state.swingState === "contact") {
2266
+ batAngle = -1.70 + finishProgress * 1.28;
2267
+ shoulderTilt = 0.20 * finishProgress;
2268
  } else if (state.swingState === "miss") {
2269
+ batAngle = -1.58 + finishProgress * 0.88;
2270
  shoulderTilt = 0.10;
2271
  } else {
2272
  batAngle = -1.24;
 
2307
  drawLimb(x + 48 * scale, shoulderY + 30 * scale, leftGlove.x, leftGlove.y, 15 * scale, "#f8fafc");
2308
  drawLimb(leftGlove.x, leftGlove.y, rightGlove.x, rightGlove.y, 15 * scale, "#f8fafc");
2309
 
2310
+ drawBat(leftGlove, rightGlove, batAngle, scale);
2311
+
2312
  ctx.fillStyle = "#facc15";
2313
  ctx.beginPath();
2314
  ctx.arc(leftGlove.x, leftGlove.y, 13 * scale, 0, Math.PI * 2);
 
2317
  ctx.arc(rightGlove.x, rightGlove.y, 13 * scale, 0, Math.PI * 2);
2318
  ctx.fill();
2319
 
 
 
2320
  ctx.fillStyle = "#f8fafc";
2321
  ctx.beginPath();
2322
  ctx.arc(x, headY, 32 * scale, 0, Math.PI * 2);
 
2365
  drawParticles();
2366
  drawStumps(width / 2, pitchPoint(0.89, 0).y + 26, 1.15);
2367
  drawBatter(now);
2368
+ drawReviewBall(now);
2369
  drawScoreState();
2370
  drawFlash();
2371
  }