ElementalAlchemist 3 weeks ago committed by GitHub
commit c49fa80aa1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,9 @@
#scale {
display: block;
margin-top: 20px;
}
.settings-disclaimer {
font-size: 75%;
font-style: italic;
}

@ -1,54 +1,24 @@
<!doctype html> <!doctype html>
<html> <html>
<head>
<style> <meta charset="utf-8" />
#road-container { <title>Bus Progress</title>
width: 100%; <link rel="stylesheet" href="drive.css" />
height: 100px; <script src="drive.js"></script>
left: 0; </head>
top: 0; <body>
position: absolute; <canvas id="road" width="1580" height="62"></canvas>
// margin: 980px 0 0 -100px; // uncomment to move to bottom of screen <label id="scale">
z-index: 3; Scale:
} <input id="scale-input" type="number" value="1" min="0.1" step="0.1" />
</label>
#road-container div { <label id="point-progress">
height: 100px; <input id="point-progress-checkbox" type="checkbox" />
width: 100%; Point Progress Mode
position: absolute; </label>
background-position: 0px 0px; <p class="settings-disclaimer">
} Settings modifications will be applied on the next update, which occur every 2.5 seconds.
</p>
#timeofday-left { <p class="settings-disclaimer">The scale setting is ignored in Point Progress Mode.</p>
z-index: 5; </body>
} </html>
#timeofday-right {
z-index: 4;
}
#stops {
z-index: 6;
background-image: url(stops.png);
}
#bus {
background-repeat: no-repeat;
margin-left: 27px;
z-index: 8;
}
</style>
<body>
<div id="road-container">
<div id="bus"></div>
<div id="timeofday-left"></div>
<div id="timeofday-right"></div>
<div id="stops"></div>
</div>
<script src="drive.js"></script>
</body>

@ -1,122 +1,254 @@
const COLORS = {
const PAGE_WIDTH = 1920; day: {
const MINUTES_PER_PAGE = 60; sky: "#41cee2",
const POINT_WIDTH = PAGE_WIDTH * 8 * 60 / MINUTES_PER_PAGE; ground: "#e5931b",
const MILES_PER_PAGE = 45; surface: "#b77616",
const BUS_POSITION_X = 93; },
const BASE_ODO = 109.3; dusk: {
const UPDATE_INTERVAL_MS = 5000 sky: "#db92be",
const WUBLOADER_URL = ""; ground: "#dd926a",
const SKY_URLS = { surface: "#b17555",
day: "db_day.png", },
dawn: "db_dawn.png", night: {
dusk: "db_dusk.png", sky: "#121336",
night: "db_night.png", ground: "#30201a",
}; surface: "#261a15",
const BUS_URLS = { },
day: "bus_day.png", dawn: {
dawn: "bus_day.png", sky: "#2b2f87",
dusk: "bus_day.png", ground: "#724d41",
night: "bus_night.png", surface: "#5b3e34",
},
}; };
function setSkyElements(left, right, timeToTransition) { // The width from the left side of the bus image to the front of the bus
const leftElement = document.getElementById("timeofday-left"); const BUS_FRONT_OFFSET = 72;
const rightElement = document.getElementById("timeofday-right");
const busElement = document.getElementById("bus");
leftElement.style.backgroundImage = `url(${SKY_URLS[left]})`; // Start time of each day phase
rightElement.style.backgroundImage = `url(${SKY_URLS[right]})`; const DAY_START_MINUTES = 450;
const DUSK_START_MINUTES = 1140;
const NIGHT_START_MINUTES = 1200;
const DAWN_START_MINUTES = 400;
if (left === right) { const BUS_STOP_OFFSET = 8;
leftElement.style.width = "100%"; const POINT_OFFSET = 17;
} else {
const transitionPercent = timeToTransition / MINUTES_PER_PAGE;
leftElement.style.width = `${transitionPercent * 100}%`
}
bus.style.backgroundImage = `url(${BUS_URLS[left]})`; // Bus stop positions are recorded in miles with the 0 position
} // at route start. This array can be looped every point.
const BUS_STOP_POSITIONS = [1, 55.2, 125.4, 166.3, 233.9, 295.2];
const BUS_DAY_IMAGE = new Image();
BUS_DAY_IMAGE.src = "bus_day.png";
const BUS_NIGHT_IMAGE = new Image();
BUS_NIGHT_IMAGE.src = "bus_night.png";
const BUS_STOP_IMAGE = new Image();
BUS_STOP_IMAGE.src = "db_stop.png";
const POINT_IMAGE = new Image();
POINT_IMAGE.src = "point.png";
// This should match the HTML canvas width
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 nextSkyTransition(timeofday, clock) { function nextPhase(timeOfDay) {
switch (timeofday) { switch (timeOfDay) {
case "day":
case "dawn": case "dawn":
return "dusk";
case "dusk":
return "night";
case "night":
return "dawn";
}
}
function phaseStartTime(timeOfDay) {
switch (timeOfDay) {
case "day": case "day":
return [19 * 60, "dusk"]; // 7pm return DAY_START_MINUTES;
case "dusk": case "dusk":
return [20 * 60, "night"]; // 8pm return DUSK_START_MINUTES;
case "night": case "night":
return [6 * 60 + 40, "dawn"]; // 6:40am return NIGHT_START_MINUTES;
case "dawn":
return DAWN_START_MINUTES;
} }
} }
function setSky(timeofday, clock) { function drawBackground(context, timeOfDay, leftX, width) {
const [transition, newSky] = nextSkyTransition(timeofday, clock); const skyColor = COLORS[timeOfDay].sky;
// 1440 minutes in 24h, this code will return time remaining even if const groundColor = COLORS[timeOfDay].ground;
// the transition is in the morning and we're currently in the evening. const surfaceColor = COLORS[timeOfDay].surface;
const timeToTransition = (1440 + transition - clock) % 1440;
if (timeToTransition < MINUTES_PER_PAGE) { width = Math.ceil(width);
// Transition on screen leftX = Math.floor(leftX);
setSkyElements(timeofday, newSky, timeToTransition);
context.fillStyle = COLORS[timeOfDay].sky;
context.fillRect(leftX, 0, width, 56);
context.fillStyle = COLORS[timeOfDay].surface;
context.fillRect(leftX, 56, width, 1);
context.fillStyle = COLORS[timeOfDay].ground;
context.fillRect(leftX, 57, width, 5);
}
async function drawRoad() {
const busDataResponse = await fetch("/thrimshim/bus/buscam");
if (!busDataResponse.ok) {
return;
}
const busData = await busDataResponse.json();
const canvas = document.getElementById("road");
if (!canvas.getContext) {
return;
}
const context = canvas.getContext("2d");
// Clear the previous canvas before starting
context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 62);
const pointModeCheckbox = document.getElementById("point-progress-checkbox");
if (pointModeCheckbox.checked) {
drawRoadPoint(context, busData);
} else { } else {
// No transition on screen drawRoadDynamic(context, busData);
setSkyElements(timeofday, timeofday, undefined);
} }
} }
function setOdo(odo) { function drawRoadPoint(context, busData) {
const distancePixels = PAGE_WIDTH * (odo - BASE_ODO) / MILES_PER_PAGE; const busDistance = (busData.odometer + 250.7) % 360;
const offset = (BUS_POSITION_X - distancePixels) % POINT_WIDTH; const busRemainingDistance = 360 - busDistance;
const busRemainingDistancePixels = busRemainingDistance * PIXELS_PER_MILE;
const stopsElement = document.getElementById("stops"); const busDistancePixels = busDistance * PIXELS_PER_MILE;
stopsElement.style.backgroundPosition = `${offset}px 0px`; 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);
async function update() { for (const busStopDistance of BUS_STOP_POSITIONS) {
const busDataResponse = await fetch(`${WUBLOADER_URL}/thrimshim/bus/buscam`); const busStopPixelPosition =
if (!busDataResponse.ok) { BUS_FRONT_OFFSET + PIXELS_PER_MILE * busStopDistance - BUS_STOP_OFFSET;
return; context.drawImage(BUS_STOP_IMAGE, busStopPixelPosition, 16);
} }
const busData = await busDataResponse.json();
console.log("Got data:", busData); if (busData.timeofday === "night") {
setOdo(busData.odometer); context.drawImage(BUS_NIGHT_IMAGE, busDistancePixels, 32);
setSky(busData.timeofday, busData.clock_minutes); } else {
context.drawImage(BUS_DAY_IMAGE, busDistancePixels, 32);
}
} }
// Initial conditions, before the first refresh finishes function drawRoadDynamic(context, busData) {
setSky("day", 7 * 60); const distance = busData.odometer;
setOdo(BASE_ODO); const timeOfDay = busData.timeofday;
// Testing mode. Set true to enable. drawBackground(context, timeOfDay, 0, BUS_FRONT_OFFSET);
const test = false;
if (test) { // The default scaling factor (1) is 20 seconds per pixel at max speed.
let h = 0; // This gives us
// Set to how long 1h of in-game time should take in real time // - 3px per minute
const hourTimeMs = 1 * 1000; // - 4px per mile
// Set to how often to update the screen let scaleFactor = +document.getElementById("scale-input").value;
const interval = 30; if (scaleFactor === 0 || isNaN(scaleFactor)) {
setInterval(() => { scaleFactor = 1;
h += interval / hourTimeMs; }
setOdo(BASE_ODO + 45 * h);
if (h < 19) { const startMinute = busData.clock_minutes;
setSky("day", 60 * h);
} else { let previousTime = startMinute;
m = (h % 24) * 60; let previousTimeOfDay = timeOfDay;
let tod; let x = BUS_FRONT_OFFSET;
if (m < 6 * 60 + 40) { while (x < CANVAS_PIXEL_WIDTH) {
tod = "night"; const nextTimeOfDay = nextPhase(previousTimeOfDay);
} else if (m < 19 * 60) { const nextStartTime = phaseStartTime(nextTimeOfDay);
tod = "dawn";
} else if (m < 20 * 60) { let thisDuration = nextStartTime - previousTime;
tod = "dusk"; if (thisDuration < 0) {
} else { thisDuration += 1440;
tod = "night"; }
const pixelWidth = thisDuration * 3 * scaleFactor;
drawBackground(context, previousTimeOfDay, x, pixelWidth);
previousTime = nextStartTime;
previousTimeOfDay = nextTimeOfDay;
x += pixelWidth;
}
x = 0;
const currentPointProgress = distance % 360;
let distanceToNextPoint;
if (currentPointProgress <= 109.3) {
distanceToNextPoint = 109.3 - currentPointProgress;
} else {
distanceToNextPoint = 469.3 - currentPointProgress;
}
distanceToNextPoint += BUS_FRONT_OFFSET / (4 * scaleFactor);
if (distanceToNextPoint >= 360) {
distanceToNextPoint -= 360;
}
x += distanceToNextPoint * 4 * scaleFactor;
context.drawImage(POINT_IMAGE, x - POINT_OFFSET, 0);
while (x < CANVAS_PIXEL_WIDTH) {
x += 360 * 4 * scaleFactor;
context.drawImage(POINT_IMAGE, x - POINT_OFFSET, 0);
}
const distanceOnRoute = (distance - 109.3) % 360;
let distanceTracked = distanceOnRoute - BUS_FRONT_OFFSET / (4 * scaleFactor);
if (distanceTracked < 0) {
distanceTracked += 720;
}
x = 0;
while (x < CANVAS_PIXEL_WIDTH) {
const distanceTrackedOnRoute = distanceTracked % 360;
let nextBusStopPosition = null;
for (const busStopPosition of BUS_STOP_POSITIONS) {
if (busStopPosition >= distanceTrackedOnRoute + 0.05) {
nextBusStopPosition = busStopPosition;
break;
} }
setSky(tod, m);
} }
}, interval); if (nextBusStopPosition === null) {
} else { nextBusStopPosition = 360 + BUS_STOP_POSITIONS[0];
// Do first update immediately, then every UPDATE_INTERVAL_MS }
setInterval(update, UPDATE_INTERVAL_MS); const nextBusStopDistance = nextBusStopPosition - distanceTrackedOnRoute;
update(); distanceTracked += nextBusStopDistance;
x += nextBusStopDistance * 4 * scaleFactor;
context.drawImage(BUS_STOP_IMAGE, x - BUS_STOP_OFFSET, 16);
}
if (timeOfDay === "night") {
context.drawImage(BUS_NIGHT_IMAGE, 0, 32);
} else {
context.drawImage(BUS_DAY_IMAGE, 0, 32);
}
} }
window.addEventListener("DOMContentLoaded", (event) => {
drawRoad();
setInterval(drawRoad, 2500);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Loading…
Cancel
Save