From 96a71c351969f653320e9963ea2af78827d8d19c Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Fri, 15 Nov 2024 03:32:44 -0600 Subject: [PATCH] Add point progress mode Adds a new mode that makes the full width be exactly one point, and the bus drives across the canvas. This also resizes the canvas to the requested size (1580x62). --- thrimbletrimmer/driveclock/bus_day.png | Bin 439 -> 4492 bytes thrimbletrimmer/driveclock/bus_night.png | Bin 520 -> 4545 bytes thrimbletrimmer/driveclock/db_stop.png | Bin 173 -> 4306 bytes thrimbletrimmer/driveclock/drive.css | 2 +- thrimbletrimmer/driveclock/drive.html | 11 ++- thrimbletrimmer/driveclock/drive.js | 88 ++++++++++++++++------- thrimbletrimmer/driveclock/point.png | Bin 397 -> 4455 bytes 7 files changed, 73 insertions(+), 28 deletions(-) diff --git a/thrimbletrimmer/driveclock/bus_day.png b/thrimbletrimmer/driveclock/bus_day.png index c85bccd3bbb8ca3508cb50451d0325fe9eb18f6d..02398106bfd39172af4bd731f83e7e037aff58dc 100644 GIT binary patch literal 4492 zcmeHLe{dAl9p6htXn>FiZBr23*%(l9Z*TX0?sA9uaS1t-i)V6@ATu?4d+)-+-R*h1 zm%BuMglKHgKr2Ekibg;z6^v5AR3eBeF+(lIG96o*vC!0Uu*2|U2En$H^t*ePOEPuF z$xQt(clY*v-+SNh=Y8Mjec$))-6l`fvYAZoO|Zi-fSfIKfHZv*+Bluic7 zv@Qdi3H44W49Ih!eglg5ljK~-K~`VWw6``hv_|S>F1H6~^t3^5w1K^WF>y4_(PrFW z;7k_IY=ilAua0`8BE7EQ_2A!AVo8a|RZ@aSqEbK%A`DCHT=$%#{I`X7_C2=p$nNdg zjXAleJTG6`O>8-mRk|-{b=&g!mHJz)3un5Xd3Mi*MZzZdelzQX+Uxlp3p$8}bWP{% zzB%^(KRed0J@)Li`>)6ww&)5Z=ES0${vF}Elk-j*U;6nUnmTR04{j}9TV>}zp0eq# zclAAAfB5djw>ulkIx3rAu@&DxXGz`V`sIvwP#oO1d-b*}XO13-{B7{S^3?}o4-NHt ze!OzU=iKSsyAQS(wCCV^o@VZQJ7>=$`F+k8Z_HjW#P?HMD;C!NsN>G*`-`|5+? z3-Rs%zxu2@tDxob4+nqt;AdX6;D8fy8liomk5IJUK!rloSPNMxZ3K{%L*Hm2dz&JNsz$ODiwdZ; z5rvifu@HVCZ6Aq6gW7WZ0)>Jo1gbL3N{^dTJ#lFZm4XH_6w$1J>~Wfk7#JmMoNwxg zww$q!fO#5soc74tHD%CpyE&&M#8mfOP6wgR&-o=m^mCdhSZQ;BVa=oXoFRgGguH^spO_ zlb=_3C$As?P3K|nNFF8)7S51#Uxjl^elgJWZQAPL!R??ATV5{8Fn^QQl{`^3=<#GL z*$j%>QNnTUP;k7ET!PFuBERM*U?qoyReZPsLH0<6>qx)&4WYnVO|*#?EF`iBYA7%? z$(mUkX%>*hELbh9(U#o5G`cJWlsF$nMGe3sa0TJ1eM8~*Xd89!cwgeHkh%*%7)jG4 zZ5|VG(~YYJnM4A>?6AbCM9q|(VS#ToefUA|I#@m)@V|3+8V=yeK(;a5-`E=v#V4SjIYs`XTQOSLN( z*4o^6Lcb0DwQq5J#L&9COH(meoQp?`cD>f(U6{2jvQgQXk zu7!!Cxou}XpFVP~uz2&v-uBuxo3DSh!TiEYxuNdN$Np5mpyMFBLpBa!v95w6nU4?s Q16G2$N~@e*#r3WK1;ol7MF0Q* delta 393 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 N?0H4D-{fp;{125?FQxzh delta 475 zcmX@8+`*#R8Q|y6%O%Cdz`(%k>ERLtq*FkcgBeJ=+oYxeDZc=p5ZB2v0xE1xO-&sO zS5Ed3kgl(-tpzd{E?gKTdcy&zNTejlFBnKOFlZUtJ2*HbHgwNjv2yF){a#jobr~2K zLp@y_Ln`LHz4p4OSwVm`AzOWAu35Rk+28v!cIs*vI(0=YFaFlfJlWT-uTgThMI@Gt!S#;#BS=SwPO~XE)NiM083MXr2F0Ou1RV(t$^U3LH z6}2iA^PISNc4{p-5+NFtlA^r-{{Fh}>3=@Dvfs4}dcNetg3rl~DnWI^6MvuGzQUrj z>$JD<&YM2V{TB&7=TS)0%)NcfBXP=^1S`wVs^}-LJcaJA-*-`3wfKgg-=hDM>f0ox zJQn?48arM7>5~t0)AV19yP78_FngHs$rR)^nZWgOTj&KVb4K4n$A-}bE? zax&$rM(@p~e)`6RevApf^K!OSm+U{CFUOAstGs$+5*Sk)^Um*j-P~smc6zptKUuN| z<@3uv-mo^^@m1C4+w$kHe=gjAd&SNAU00c9L7_5XzjGY}=eMW&I-9J$Ktb>6>gTe~ HDWM4fC&0s? diff --git a/thrimbletrimmer/driveclock/db_stop.png b/thrimbletrimmer/driveclock/db_stop.png index 6bc52e82e3230b02181c938f0a23650c56d2436b..a3f3fe384c1abf23c5ca3326e4d9e458b3bc82cd 100644 GIT binary patch literal 4306 zcmeHKdvFs)7{3%M5DFFnMFBYviqPS5k6a!-6NEM`4K$_F07Yhm&D~x(NOCc`HYtLZ zDhk@c7wCv64Ez%j6dzcnC~rHAB96?U4k`@eFzE1d#@G0ues@XJc67$+jQ-bjHv8@F z@B8iUZ@>N0U0+i@Ynb&mD?t#${FT0WP~Qk;%D@5eed?a~e}U>#T<2H({$gS%L^jMiJXIES<&tk1t*{_+qzd| zF#WV)wg*@qQOn9|{AFciEUqbP7!yQl%j&1Sl}BcbJGNx;-tC))Hx`aKUh~qW?bL?7 z*79`W^0f=bSK0n-nLNz@_!B$M-5a|mz486I^SfSs_*HW7gU#-{+*9{euJ4>3s!XGEL-rh=0=4!W&JT}VH@ws=^s`sDx zVcONiLmMnb8h`N4!j2cBbw|e@wZHh>Crw-3hi5-ix~kfPKFwc0;XPaXA0NJVanIKJ ziq@)?FS|>pjk>Gu%F+dV@Vt6Hy?yz{t0(vEihXl_*MjA{8s=UqBPPAE zu4r8$x#Lm3cu(Pu`4f-%Hvc^G_UovFd1lV!g}1hj8=5X%`|YoXI?pBB6tw)afVHUk z$}MNEj~UR?KI5(EnX?`5eC@I1ojkbd^v?^&9=vqtcR#&zvp4VXnS;j~7C-w-^GD;C zUi3d-aQx{!;m@^s*B-1myK%q`+L{j>ZEdHgS5IgzXxOyRGJN=&A1M6#1N+jG7xq(y8<)!wD&_#&lJSk_IOVX$`uUqF|itT3;j< z2;}IaiL45s2a`fEhO@CuB*OIcNa*E_K$2b1KYApBO)<>O!wIb+F5&V<9MvcGq>!bY zf2<)MHn$^73=ZQ6Koc-4*K11S#^pQ=3F_5I%=7}Wdm(jI=@zTk+Kdr%J3SWy?m6CG z=&rq+U_b=|qEC|=jOF=#UdotXlr>3}MblL5C2k3YcpAw_q6Or1(IHN8(%6CQg3GBW zg5BMN${$VWC@Nut3dn6L@NlfeW8_k31=}$#@NR`J5m-Ad$dbgeEbovwdk>1aaTTHx zg?p~bppt<~0lY(UOSH?b2(%zcJRQOuOLKM(g;0s)atN|XB}-z37LOn}PBnt+F%yf{ zn*)a6;>;Sqm*Q<~cTY_i=?XY_sVX(vkm?=?su5hPBSTHjW#^m@-pRUP8kdXj1}(tx z1Vp02Y35FI#5fiaHUq*UBTj+9?1#OGWpRvjEgsagu$RhZ-P~{hu9J*(( zY$1UfaYkX_$_z=%P_!OH_Q-~7SHJoXp&+<9UXVFEZI=ZO0>CNJA;pPk#o=&rPFZo5 zxLl?RIrfC6=t&gEGwVS|&aw*(Huu}jxNDmS;3g>h8ZJg zbnV!~{7n;&3Fwhzz%SDW$qRBJlS_tK&0xL%=O=p>|K}7W**{62^zE0cU#>nW&?j(z zboI;CCk6Th?vJkjOfGBpa|%b{zn~;MmOgGwe+3V%(KS`Ua;Q5yJK_8CxJj#5uLgVm z{P{~x?loTS7JXiIEivQ57mCpc&6(vR*5!{kUgJYzm5UMtF?6I+EX3w5W1w@8?hlj? zx;XU48zwQcj=pJ7SNMH1gJVufOJ@+1i;4#C4u=aAiz-R^+lX~eXK5&1ymsf#;#Z2< zbj!?i-sZ}5ah-3-w3b)KNBP6HcP|$dOdWFMNegjw!yWCveJ;9fRs>xPl)UP|Nnu^w=ycVwY5NYXlUr#r)wqwg&0eM z{DK)Ap4~_Tay&g<978JNk|icK1O^B=acgQeMml*pC@_cwFfqESv~?`#?&w(1!O+Yh zv4dkl=Z=mBSC)fHFK#_lJN&lkHXqwT#w#g3Ms4D13=Co?6t_%FY>)+-%HZkh=d#Wz Gp$P!9o;bAt diff --git a/thrimbletrimmer/driveclock/drive.css b/thrimbletrimmer/driveclock/drive.css index 368695c..fb14518 100644 --- a/thrimbletrimmer/driveclock/drive.css +++ b/thrimbletrimmer/driveclock/drive.css @@ -3,7 +3,7 @@ margin-top: 20px; } -#scale-disclaimer { +.settings-disclaimer { font-size: 75%; font-style: italic; } diff --git a/thrimbletrimmer/driveclock/drive.html b/thrimbletrimmer/driveclock/drive.html index d73e794..49982c5 100644 --- a/thrimbletrimmer/driveclock/drive.html +++ b/thrimbletrimmer/driveclock/drive.html @@ -7,13 +7,18 @@ - + -

- Scale modifications will be applied on the next update, which occur every 2.5 seconds. + +

+ Settings modifications will be applied on the next update, which occur every 2.5 seconds.

+

The scale setting is ignored in Point Progress Mode.

diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index 58c029d..114db63 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -21,10 +21,8 @@ const COLORS = { }, }; -const KEY_OUT_COLOR = "#2b6ec6"; - // The width from the left side of the bus image to the front of the bus -const BUS_FRONT_OFFSET = 73; +const BUS_FRONT_OFFSET = 72; // Start time of each day phase const DAY_START_MINUTES = 450; @@ -49,7 +47,12 @@ const POINT_IMAGE = new Image(); POINT_IMAGE.src = "point.png"; // This should match the HTML canvas width -const CANVAS_PIXEL_WIDTH = 1920; +const CANVAS_PIXEL_WIDTH = 1580; + +const BUS_TRAVEL_WIDTH = CANVAS_PIXEL_WIDTH - BUS_FRONT_OFFSET; +const PIXELS_PER_MILE = BUS_TRAVEL_WIDTH / 360; +const PIXELS_PER_MINUTE = BUS_TRAVEL_WIDTH / 480; +const FULL_SPEED_MILES_PER_MINUTE = 0.75; function nextPhase(timeOfDay) { switch (timeOfDay) { @@ -81,17 +84,12 @@ function drawBackground(context, timeOfDay, leftX, width) { const groundColor = COLORS[timeOfDay].ground; const surfaceColor = COLORS[timeOfDay].surface; - context.fillStyle = KEY_OUT_COLOR; - context.fillRect(leftX, 80, width, 20); context.fillStyle = COLORS[timeOfDay].sky; - context.fillRect(leftX, 0, width, 80); + context.fillRect(leftX, 0, width, 56); context.fillStyle = COLORS[timeOfDay].surface; - context.fillRect(leftX, 80, width, 1); + context.fillRect(leftX, 56, width, 1); context.fillStyle = COLORS[timeOfDay].ground; - context.fillRect(leftX, 81, width, 7); - context.fillRect(leftX, 89, width, 3); - context.fillRect(leftX, 94, width, 2); - context.fillRect(leftX, 99, width, 1); + context.fillRect(leftX, 57, width, 5); } async function drawRoad() { @@ -108,19 +106,62 @@ async function drawRoad() { const context = canvas.getContext("2d"); // Clear the previous canvas before starting - context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 100); - // Background the whole thing as the key-out color in case we need to bail - // out before drawing (e.g. we're in a non-DB game menu) - context.fillStyle = KEY_OUT_COLOR; - context.fillRect(0, 0, CANVAS_PIXEL_WIDTH, 100); + context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 62); + + const pointModeCheckbox = document.getElementById("point-progress-checkbox"); + if (pointModeCheckbox.checked) { + drawRoadPoint(context, busData); + } else { + drawRoadDynamic(context, busData); + } +} + +function drawRoadPoint(context, busData) { + const busDistance = (busData.odometer + 250.7) % 360; + const busRemainingDistance = 360 - busDistance; + const busRemainingDistancePixels = busRemainingDistance * PIXELS_PER_MILE; + + const busDistancePixels = busDistance * PIXELS_PER_MILE; + let x = busDistancePixels + BUS_FRONT_OFFSET; + drawBackground(context, busData.timeofday, 0, x); + let currentTimeOfDay = busData.timeofday; + let currentTime = busData.clock_minutes; + while (x < CANVAS_PIXEL_WIDTH) { + const nextTimeOfDay = nextPhase(currentTimeOfDay); + const nextStartTime = phaseStartTime(nextTimeOfDay); + + let thisDuration = nextStartTime - currentTime; + if (thisDuration < 0) { + thisDuration += 1440; + } + const pixelWidth = thisDuration * PIXELS_PER_MINUTE; + drawBackground(context, currentTimeOfDay, x, pixelWidth); + x += pixelWidth; + currentTimeOfDay = nextTimeOfDay; + currentTime += thisDuration; + } + + context.drawImage(POINT_IMAGE, CANVAS_PIXEL_WIDTH - POINT_OFFSET, 0); + + for (const busStopDistance of BUS_STOP_POSITIONS) { + const busStopPixelPosition = + BUS_FRONT_OFFSET + PIXELS_PER_MILE * busStopDistance - BUS_STOP_OFFSET; + context.drawImage(BUS_STOP_IMAGE, busStopPixelPosition, 16); + } + + if (busData.timeofday === "night") { + context.drawImage(BUS_NIGHT_IMAGE, busDistancePixels, 32); + } else { + context.drawImage(BUS_DAY_IMAGE, busDistancePixels, 32); + } +} - const currentTime = busData.clock_minutes; +function drawRoadDynamic(context, busData) { const distance = busData.odometer; const timeOfDay = busData.timeofday; drawBackground(context, timeOfDay, 0, BUS_FRONT_OFFSET); - const maxWidth = CANVAS_PIXEL_WIDTH - BUS_FRONT_OFFSET; // The default scaling factor (1) is 20 seconds per pixel at max speed. // This gives us // - 3px per minute @@ -131,7 +172,7 @@ async function drawRoad() { } const startMinute = busData.clock_minutes; - const timeDuration = maxWidth / (3 * scaleFactor); + const timeDuration = BUS_TRAVEL_WIDTH / (3 * scaleFactor); let previousTime = startMinute; let previousTimeOfDay = timeOfDay; @@ -146,7 +187,6 @@ async function drawRoad() { thisDuration += 1440; } - // TODO Figure out scaling factor const pixelWidth = thisDuration * 3 * scaleFactor; drawBackground(context, previousTimeOfDay, x, pixelWidth); @@ -198,13 +238,13 @@ async function drawRoad() { const nextBusStopDistance = nextBusStopPosition - distanceTrackedOnRoute; distanceTracked += nextBusStopDistance; x += nextBusStopDistance * 4 * scaleFactor; - context.drawImage(BUS_STOP_IMAGE, x - BUS_STOP_OFFSET, 0); + context.drawImage(BUS_STOP_IMAGE, x - BUS_STOP_OFFSET, 16); } if (timeOfDay === "night") { - context.drawImage(BUS_NIGHT_IMAGE, 0, 0); + context.drawImage(BUS_NIGHT_IMAGE, 0, 32); } else { - context.drawImage(BUS_DAY_IMAGE, 0, 0); + context.drawImage(BUS_DAY_IMAGE, 0, 32); } } diff --git a/thrimbletrimmer/driveclock/point.png b/thrimbletrimmer/driveclock/point.png index 601c57ab0bdcb430f12d4cf6358c7fcf7e57c927..a1516883022ed0156945cf4382904f6f246afef4 100644 GIT binary patch literal 4455 zcmeHKeNYtV8NWLa@%Si4ZB_i(EWxPF-tOMr-pA(7ARNjmM^G+!N~U#h_dQtPZZEeB zcc3*u!J?u0Fq#ibDkC$kqa@Lq$S7(MgN&1*+We7^GDb@sgB>d}wXv-R`|jR>FwIPd zndCq2=DqjX_x(M;_j!Ns^FDLCtFT~giguw^rBbCh^BfytkH{@C0se}$wRd568F-vN zr*ow$5k5eqI$ougF|es8*cx@5Dh`hAgzYhC%NV&WRL@ZQ7HFr(?7h%l9>Zv$O{z7j z6xg@Irib=i*dK1YypU@j=lB7urAqd)N zrb!b-tJ@w$Bs1MVHsd5@Y2b?Uk-@-A5ak!(Yyu>F(Emda?1<{&%(KHr>r=S<-?oBa05q>78rq z``o^L`w7!k`8fS!!*AaBW6c3e&$>O?+Y4;WY>JK9tDvy#m> z=>FMH9Ba;OPD9#v>Q^33Ykw}I*RlVbIZH;FK77ymWt$eXEJ`|*-E{q5=N?`Qclwy} zPhHx~y4yb)7@Z&2(3N#OFmSc+#1)%bbE$jZXWwi}@4ofq&3~Twv0c+M(A`^A^7_5H zPZn<-a=w{-@imR{+a}GO9c!-cjY|;f&r+S8pRFobT9;h4@02<A<2P3@OBY9l;G%NUZ3@3PjF5(Z$2d1(;6A3bG1&|OgDCGlo zbhzsy6yZ5Lx`}cTu3!!*#?WAV!^0M_{=TzFLp2(jvAH=c6||*2E~_lN~LSq#%vs;c!?N zHt2*3P zv)+JNO+E_-D3&o=eI_Hv7$&oef=>!FA+Wj>>Ik)h>8WUiELRR{#bk8249KSd1;a=Z zBdIaLV6rKK;js7EaLXm-f(J@%OU@gSQi(yPNx$OLapV(RafGa@i z=|!fb@3dUga!pBrDS@Z6Yg(=;DKI7Qbas7ja%msEPJsaY7ZipsOYIA~FTfYA1n>H_ z4%I{X+ji#nufvg9!Mx3)N|iK6Zfe#3*7}g_Q+V;hb zhAb@MdC{LgQkeC`=R3c;@qA+Mum4?SITC+&Ih9;`p|!xe?%H3H>lPjR)0Q&Uk@cV_ z!BPMC(Y}lldR>1-W`)l4XXk#+oCnQh@;lpZu2PMh*XLi>H>A$0J6uz9Z*=(h#Vy6y zbL?sV`-2NsHV?k_bxZr;+gG+d)_98_P1=(_Z$TS(_PsCSAMfqmbEf_4-vx)`m#B8% z_|ld!e!OXlUqyvaNzp=SK2b0Hr~4)Uydxl(Q$Qz!}z7mZCCEqe=r2nH@rKG fUvZ{<#g4eUZMD?E&CxfZL@H-)f#bdGtquPLLP`lo literal 397 zcmeAS@N?(olHy`uVBq!ia0vp^N1P;jIbW(++GCit4OaVeV;+Jy2Y-@A-4dhqi|l zTqanV*5ymvmU*8rlMJpsVZ8d-^|lVBsH8gY$zf_5T}_`0=e61XemsrWB;<>~O7U~W zM@!8wyG%BGGV^=Jd7n3X)iUP)cmJ}s$zqYa%%WQzjp=@2uCG4+*;jw)(w>rdna=Ot zLZ950S8O)hJ4^0ko9To3$!(f8LsJnRn7_(&^6^ iCf=3;1|ceF=;(O&`&pHFaLbyNAQ?|rKbLh*2~7ZGXR9~>