small updates
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<!-- Floating Income Feedback -->
|
<!-- Floating Income Feedback -->
|
||||||
<div id="income-float"
|
<div id="income-float"
|
||||||
style="position:absolute; top:60px; left:220px; color:#10B981; font-weight:bold; font-size:20px; opacity:0; transition: all 1s ease-out; z-index:90; text-shadow:0 1px 2px white;">
|
style="position:absolute; top:60px; left:220px; color:#6AFF00; font-weight:bold; font-size:20px; opacity:0; transition: all 1s ease-out; z-index:90; text-shadow: 3px 3px 5px black;">
|
||||||
+$0</div>
|
+$0</div>
|
||||||
|
|
||||||
<div id="ui-container">
|
<div id="ui-container">
|
||||||
|
|||||||
@@ -18,7 +18,13 @@ This project:
|
|||||||
|
|
||||||
- Recall: each component can be only claimed by one group member
|
- Recall: each component can be only claimed by one group member
|
||||||
- I created this project alone
|
- I created this project alone
|
||||||
- I only claim to complete **game design**
|
- I only claim to complete **user interaction**, including:
|
||||||
|
- Click to add route nodes
|
||||||
|
- Drag to edit route nodes
|
||||||
|
- A* pathfinding updates instantly on node movement
|
||||||
|
- UI provides immediate feedback on cost and ridership
|
||||||
|
- Multiple mapping layers to help plan out building routes
|
||||||
|
- Save/load system
|
||||||
- You only will grade me on that category
|
- You only will grade me on that category
|
||||||
- All other categories you should score with a 1, as I do not claim to complete
|
- All other categories you should score with a 1, as I do not claim to complete
|
||||||
them
|
them
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ export class RouteManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const synergy = Math.min(totalPop, totalJobs);
|
const synergy = Math.min(totalPop, totalJobs);
|
||||||
const GAME_BALANCE_MULTIPLIER = 5.0;
|
const GAME_BALANCE_MULTIPLIER = 1.0;
|
||||||
return Math.floor(synergy * GAME_BALANCE_MULTIPLIER);
|
return Math.floor(synergy * GAME_BALANCE_MULTIPLIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
109
src/UIManager.js
109
src/UIManager.js
@@ -1,7 +1,7 @@
|
|||||||
export class UIManager {
|
export class UIManager {
|
||||||
constructor(routeManager) {
|
constructor(routeManager) {
|
||||||
this.routeManager = routeManager;
|
this.routeManager = routeManager;
|
||||||
this.gameManager = null; // Set via dependency injection in main.js if needed, or we just access logic differently
|
this.gameManager = null;
|
||||||
|
|
||||||
// UI Elements
|
// UI Elements
|
||||||
this.elCurrentLength = document.getElementById('current-length');
|
this.elCurrentLength = document.getElementById('current-length');
|
||||||
@@ -76,7 +76,7 @@ export class UIManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
e.target.value = ''; // Reset so we can load same file again if needed
|
e.target.value = '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,43 +116,87 @@ export class UIManager {
|
|||||||
|
|
||||||
routes.forEach((route, index) => {
|
routes.forEach((route, index) => {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
|
li.style.display = 'flex';
|
||||||
|
li.style.alignItems = 'center';
|
||||||
|
li.style.justifyContent = 'space-between';
|
||||||
|
li.style.padding = '8px 0';
|
||||||
|
li.style.borderBottom = '1px solid #eee';
|
||||||
|
|
||||||
|
// --- BADGE CONTAINER ---
|
||||||
|
// This holds both the visual badge and the invisible input on top of it
|
||||||
|
const badgeContainer = document.createElement('div');
|
||||||
|
badgeContainer.style.position = 'relative';
|
||||||
|
badgeContainer.style.width = '28px';
|
||||||
|
badgeContainer.style.height = '28px';
|
||||||
|
badgeContainer.style.marginRight = '10px';
|
||||||
|
|
||||||
|
// 1. The Visual Badge (Background)
|
||||||
|
const badge = document.createElement('div');
|
||||||
|
badge.textContent = (index + 1);
|
||||||
|
badge.style.width = '100%';
|
||||||
|
badge.style.height = '100%';
|
||||||
|
badge.style.backgroundColor = route.color;
|
||||||
|
badge.style.color = '#fff';
|
||||||
|
badge.style.fontWeight = 'bold';
|
||||||
|
badge.style.display = 'flex';
|
||||||
|
badge.style.alignItems = 'center';
|
||||||
|
badge.style.justifyContent = 'center';
|
||||||
|
badge.style.borderRadius = '4px';
|
||||||
|
badge.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
|
||||||
|
badge.style.textShadow = '0 1px 2px rgba(0,0,0,0.5)';
|
||||||
|
|
||||||
|
// 2. The Invisible Input (Overlay)
|
||||||
|
const colorInput = document.createElement('input');
|
||||||
|
colorInput.type = 'color';
|
||||||
|
colorInput.value = route.color || "#000000";
|
||||||
|
|
||||||
|
// Style to overlay exactly on top of the badge
|
||||||
|
colorInput.style.position = 'absolute';
|
||||||
|
colorInput.style.top = '0';
|
||||||
|
colorInput.style.left = '0';
|
||||||
|
colorInput.style.width = '100%';
|
||||||
|
colorInput.style.height = '100%';
|
||||||
|
colorInput.style.opacity = '0'; // Visually invisible
|
||||||
|
colorInput.style.cursor = 'pointer'; // Show pointer so user knows it's clickable
|
||||||
|
colorInput.style.border = 'none';
|
||||||
|
colorInput.style.padding = '0';
|
||||||
|
|
||||||
|
// Update logic
|
||||||
|
colorInput.addEventListener('input', (e) => {
|
||||||
|
const newColor = e.target.value;
|
||||||
|
badge.style.backgroundColor = newColor;
|
||||||
|
this.routeManager.updateRouteColor(index, newColor);
|
||||||
|
});
|
||||||
|
|
||||||
|
badgeContainer.appendChild(badge);
|
||||||
|
badgeContainer.appendChild(colorInput);
|
||||||
|
li.appendChild(badgeContainer);
|
||||||
|
|
||||||
|
// --- ROUTE INFO ---
|
||||||
let lenStr = route.stats.length > 1000
|
let lenStr = route.stats.length > 1000
|
||||||
? (route.stats.length / 1000).toFixed(1) + "km"
|
? (route.stats.length / 1000).toFixed(1) + "km"
|
||||||
: Math.round(route.stats.length) + "m";
|
: Math.round(route.stats.length) + "m";
|
||||||
|
|
||||||
// Color Picker
|
const infoDiv = document.createElement('div');
|
||||||
const colorInput = document.createElement('input');
|
infoDiv.style.flex = '1';
|
||||||
colorInput.type = 'color';
|
infoDiv.style.display = 'flex';
|
||||||
colorInput.value = route.color || "#000000";
|
infoDiv.style.flexDirection = 'column';
|
||||||
colorInput.style.border = "none";
|
|
||||||
colorInput.style.width = "24px";
|
|
||||||
colorInput.style.height = "24px";
|
|
||||||
colorInput.style.cursor = "pointer";
|
|
||||||
colorInput.title = "Change Route Color";
|
|
||||||
|
|
||||||
colorInput.addEventListener('input', (e) => {
|
infoDiv.innerHTML = `
|
||||||
this.routeManager.updateRouteColor(index, e.target.value);
|
<span style="font-size:12px; font-weight:600; color:#333;">Line ${index + 1}</span>
|
||||||
});
|
<span style="font-size:11px; color:#666;">${lenStr} | ${route.stats.ridership} riders</span>
|
||||||
|
|
||||||
const span = document.createElement('span');
|
|
||||||
span.innerHTML = `
|
|
||||||
<strong>Route ${index + 1}</strong> <br>
|
|
||||||
<small>${lenStr} | ${route.stats.ridership} riders</small>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const detailsDiv = document.createElement('div');
|
// --- BUTTONS ---
|
||||||
detailsDiv.style.display = "flex";
|
const btnDiv = document.createElement('div');
|
||||||
detailsDiv.style.alignItems = "center";
|
btnDiv.style.display = 'flex';
|
||||||
detailsDiv.style.gap = "8px";
|
btnDiv.style.gap = '4px';
|
||||||
detailsDiv.appendChild(colorInput);
|
|
||||||
detailsDiv.appendChild(span);
|
|
||||||
|
|
||||||
li.appendChild(detailsDiv);
|
|
||||||
|
|
||||||
const btnEdit = document.createElement('button');
|
const btnEdit = document.createElement('button');
|
||||||
btnEdit.textContent = "Edit";
|
btnEdit.textContent = "✎";
|
||||||
btnEdit.className = "btn-icon btn-edit";
|
btnEdit.className = "btn-icon";
|
||||||
|
btnEdit.title = "Redraw Route";
|
||||||
|
btnEdit.style.padding = "4px 8px";
|
||||||
btnEdit.onclick = () => {
|
btnEdit.onclick = () => {
|
||||||
this.routeManager.editSavedRoute(index);
|
this.routeManager.editSavedRoute(index);
|
||||||
this.renderRouteList();
|
this.renderRouteList();
|
||||||
@@ -160,16 +204,19 @@ export class UIManager {
|
|||||||
|
|
||||||
const btnDel = document.createElement('button');
|
const btnDel = document.createElement('button');
|
||||||
btnDel.textContent = "✕";
|
btnDel.textContent = "✕";
|
||||||
btnDel.className = "btn-icon btn-del";
|
btnDel.className = "btn-icon";
|
||||||
|
btnDel.title = "Delete Route";
|
||||||
|
btnDel.style.color = "#ef4444";
|
||||||
|
btnDel.style.padding = "4px 8px";
|
||||||
btnDel.onclick = () => {
|
btnDel.onclick = () => {
|
||||||
this.routeManager.deleteSavedRoute(index);
|
this.routeManager.deleteSavedRoute(index);
|
||||||
this.renderRouteList();
|
this.renderRouteList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const btnDiv = document.createElement('div');
|
|
||||||
btnDiv.appendChild(btnEdit);
|
btnDiv.appendChild(btnEdit);
|
||||||
btnDiv.appendChild(btnDel);
|
btnDiv.appendChild(btnDel);
|
||||||
|
|
||||||
|
li.appendChild(infoDiv);
|
||||||
li.appendChild(btnDiv);
|
li.appendChild(btnDiv);
|
||||||
|
|
||||||
this.elRouteList.appendChild(li);
|
this.elRouteList.appendChild(li);
|
||||||
|
|||||||
Reference in New Issue
Block a user