Compare commits

...

6 Commits

Author SHA1 Message Date
ElementalAlchemist 75a6733362 Better align bus stop signs with the front of the bus 10 months ago
ElementalAlchemist a80f7889af Show icons for points we've driven past 10 months ago
ElementalAlchemist e440dd965c Constantize the canvas width in case we want it widened in the future 10 months ago
ElementalAlchemist 79e6e7266c Add key-out color on the ground 10 months ago
ElementalAlchemist 2d68b7805e Implement scaling 10 months ago
ElementalAlchemist 7fca10593d Implement canvas-based bus system 10 months ago

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: 173 B

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

@ -1,54 +1,19 @@
<!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="1920" height="100"></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 { <p id="scale-disclaimer">
height: 100px; Scale modifications will be applied on the next update, which occur every 10 seconds.
width: 100%; </p>
position: absolute; </body>
background-position: 0px 0px; </html>
}
#timeofday-left {
z-index: 5;
}
#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,210 @@
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) { const KEY_OUT_COLOR = "#2b6ec6";
const leftElement = document.getElementById("timeofday-left");
const rightElement = document.getElementById("timeofday-right");
const busElement = document.getElementById("bus");
leftElement.style.backgroundImage = `url(${SKY_URLS[left]})`; // The width from the left side of the bus image to the front of the bus
rightElement.style.backgroundImage = `url(${SKY_URLS[right]})`; const BUS_FRONT_OFFSET = 73;
if (left === right) { // Start time of each day phase
leftElement.style.width = "100%"; const DAY_START_MINUTES = 450;
} else { const DUSK_START_MINUTES = 1140;
const transitionPercent = timeToTransition / MINUTES_PER_PAGE; const NIGHT_START_MINUTES = 1200;
leftElement.style.width = `${transitionPercent * 100}%` const DAWN_START_MINUTES = 400;
}
bus.style.backgroundImage = `url(${BUS_URLS[left]})`; const BUS_STOP_OFFSET = 8;
} const POINT_OFFSET = 17;
function nextSkyTransition(timeofday, clock) { // Bus stop positions are recorded in miles with the 0 position
switch (timeofday) { // at route start. This array can be looped every point.
case "dawn": 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 = 1920;
function nextPhase(timeOfDay) {
switch (timeOfDay) {
case "day": case "day":
return [19 * 60, "dusk"]; // 7pm case "dawn":
return "dusk";
case "dusk": case "dusk":
return [20 * 60, "night"]; // 8pm return "night";
case "night": case "night":
return [6 * 60 + 40, "dawn"]; // 6:40am return "dawn";
} }
} }
function setSky(timeofday, clock) { function phaseStartTime(timeOfDay) {
const [transition, newSky] = nextSkyTransition(timeofday, clock); switch (timeOfDay) {
// 1440 minutes in 24h, this code will return time remaining even if case "day":
// the transition is in the morning and we're currently in the evening. return DAY_START_MINUTES;
const timeToTransition = (1440 + transition - clock) % 1440; case "dusk":
if (timeToTransition < MINUTES_PER_PAGE) { return DUSK_START_MINUTES;
// Transition on screen case "night":
setSkyElements(timeofday, newSky, timeToTransition); return NIGHT_START_MINUTES;
} else { case "dawn":
// No transition on screen return DAWN_START_MINUTES;
setSkyElements(timeofday, timeofday, undefined);
} }
} }
function setOdo(odo) { function drawBackground(context, timeOfDay, leftX, width) {
const distancePixels = PAGE_WIDTH * (odo - BASE_ODO) / MILES_PER_PAGE; const skyColor = COLORS[timeOfDay].sky;
const offset = (BUS_POSITION_X - distancePixels) % POINT_WIDTH; const groundColor = COLORS[timeOfDay].ground;
const surfaceColor = COLORS[timeOfDay].surface;
const stopsElement = document.getElementById("stops"); context.fillStyle = KEY_OUT_COLOR;
stopsElement.style.backgroundPosition = `${offset}px 0px`; context.fillRect(leftX, 80, width, 20);
context.fillStyle = COLORS[timeOfDay].sky;
context.fillRect(leftX, 0, width, 80);
context.fillStyle = COLORS[timeOfDay].surface;
context.fillRect(leftX, 80, 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);
} }
async function update() { async function drawRoad() {
const busDataResponse = await fetch(`${WUBLOADER_URL}/thrimshim/bus/buscam`); const busDataResponse = await fetch("/thrimshim/bus/buscam");
if (!busDataResponse.ok) { if (!busDataResponse.ok) {
return; return;
} }
const busData = await busDataResponse.json(); const busData = await busDataResponse.json();
console.log("Got data:", busData);
setOdo(busData.odometer);
setSky(busData.timeofday, busData.clock_minutes);
}
// Initial conditions, before the first refresh finishes const canvas = document.getElementById("road");
setSky("day", 7 * 60); if (!canvas.getContext) {
setOdo(BASE_ODO); return;
}
// Testing mode. Set true to enable. const context = canvas.getContext("2d");
const test = false;
if (test) { // Clear the previous canvas before starting
let h = 0; context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 100);
// Set to how long 1h of in-game time should take in real time
const hourTimeMs = 1 * 1000; const currentTime = busData.clock_minutes;
// Set to how often to update the screen const distance = busData.odometer;
const interval = 30; const timeOfDay = busData.timeofday;
setInterval(() => {
h += interval / hourTimeMs; drawBackground(context, timeOfDay, 0, BUS_FRONT_OFFSET);
setOdo(BASE_ODO + 45 * h);
if (h < 19) { const maxWidth = CANVAS_PIXEL_WIDTH - BUS_FRONT_OFFSET;
setSky("day", 60 * h); // The default scaling factor (1) is 20 seconds per pixel at max speed.
} else { // This gives us
m = (h % 24) * 60; // - 3px per minute
let tod; // - 4px per mile
if (m < 6 * 60 + 40) { let scaleFactor = +document.getElementById("scale-input").value;
tod = "night"; if (scaleFactor === 0 || isNaN(scaleFactor)) {
} else if (m < 19 * 60) { scaleFactor = 1;
tod = "dawn"; }
} else if (m < 20 * 60) {
tod = "dusk"; const startMinute = busData.clock_minutes;
} else { const timeDuration = maxWidth / (3 * scaleFactor);
tod = "night";
let previousTime = startMinute;
let previousTimeOfDay = timeOfDay;
let remainingDuration = timeDuration;
let x = BUS_FRONT_OFFSET;
while (remainingDuration > 0) {
const nextTimeOfDay = nextPhase(previousTimeOfDay);
const nextStartTime = phaseStartTime(nextTimeOfDay);
let thisDuration = nextStartTime - previousTime;
if (thisDuration < 0) {
thisDuration += 1440;
}
// TODO Figure out scaling factor
const pixelWidth = thisDuration * 3 * scaleFactor;
drawBackground(context, previousTimeOfDay, x, pixelWidth);
remainingDuration -= thisDuration;
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 + 1) {
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, 0);
}
if (timeOfDay === "night") {
context.drawImage(BUS_NIGHT_IMAGE, 0, 0);
} else {
context.drawImage(BUS_DAY_IMAGE, 0, 0);
}
} }
window.addEventListener("DOMContentLoaded", (event) => {
drawRoad();
setInterval(drawRoad, 10000);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Loading…
Cancel
Save