Files
ac-server-scripts/index.html
2025-11-22 01:08:47 +01:00

483 lines
15 KiB
HTML

<html>
<style>
body {
background: #111;
color: #f2f2f2;
font-family: "Press Start 2P", monospace;
/* retro pixel font */
padding: 20px;
text-shadow: 0 0 8px #ff00ff;
}
/* Import pixel font */
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
h1 {
color: #00eaff;
margin-top: 25px;
margin-bottom: 10px;
/* font-size: 18px; */
text-shadow: 0 0 6px #00eaff;
}
/* Retro select elements */
select {
background: #222;
color: #00f6ff;
border: 2px solid #00eaff;
padding: 6px 8px;
font-size: 12px;
font-family: "Press Start 2P", monospace;
border-radius: 4px;
cursor: pointer;
text-shadow: 0 0 6px #00eaff;
margin-right: 6px;
}
select:hover {
box-shadow: 0 0 10px #00eaff;
}
/* Buttons */
button {
background: #330044;
color: #ff00ff;
border: 2px solid #ff00ff;
padding: 8px 12px;
margin-top: 10px;
font-family: "Press Start 2P", monospace;
font-size: 12px;
cursor: pointer;
border-radius: 4px;
transition: 0.15s;
text-shadow: 0 0 8px #ff00ff;
}
button:hover {
background: #550066;
box-shadow: 0 0 12px #ff00ff, 0 0 20px #ff00ff inset;
}
/* Car block layout */
.car-block {
margin-top: 15px;
padding: 12px;
background: #1a001f;
border: 2px solid #660099;
border-radius: 6px;
box-shadow: 0 0 12px #660099;
animation-name: appear_animation;
animation-duration: 0.5s;
}
.car-block.remove-anim {
animation: disappear_animation 0.35s forwards;
}
.car-select,
.skin-select {
min-width: 140px;
}
/* Car list container */
#carList {
margin-top: 20px;
}
/* Image previews */
.parent {
display: flex;
gap: 20px;
margin-top: 10px;
}
#preview,
#outline {
border: 2px solid #00eaff;
box-shadow: 0 0 12px #00eaff;
}
/* Glow for images on hover */
img:hover {
box-shadow: 0 0 16px #00eaff;
}
@keyframes appear_animation {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes disappear_animation {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.9);
}
}
</style>
<body>
<h1>Choose a track</h1>
<select id="trackSelect">
{{tracks}}
</select>
<select id="configSelect">
{{configs}}
</select>
<button onclick="apply()">Change track</button>
<br><br>
<div class="parent">
<img id="preview" src="" width="400" style="border:1px solid #444">
<img id="outline" src="" width="400">
</div>
<h1>Choose gamemode</h1>
<p>Set to 0 to disable</p>
<input type="number" class="practice-minutes" placeholder="Practice minutes">
<input type="number" class="qualify-minutes" placeholder="Qualify minutes">
<input type="number" class="race-laps" placeholder="Race laps">
<button onclick="applyGamemodes()">Apply</button>
<h1>Choose cars</h1>
<button id="addCarBtn">Add Car</button>
<button id="applyCarsBtn">Change cars</button>
<div id="carList"></div>
<script>
const trackSelect = document.getElementById("trackSelect");
const configSelect = document.getElementById("configSelect");
const previewImg = document.getElementById("preview");
const outlineImg = document.getElementById("outline");
function updateCurrentTrack() {
fetch('/currenttrack')
.then(r => r.json())
.then(async data => {
trackSelect.value = data.track;
await updateTrack(true);
configSelect.value = data.config;
updateTrackImages();
});
}
function updateTrack(shouldUpdateConfig = true, preselectConfig = null) {
return fetch(`/track/${trackSelect.value}`)
.then(res => res.json())
.then(data => {
previewImg.src = data.image;
outlineImg.src = data.outline;
if (shouldUpdateConfig) {
configSelect.innerHTML = "";
data.configs.forEach(c => {
let opt = document.createElement("option");
opt.value = c;
opt.textContent = c;
configSelect.appendChild(opt);
});
}
if (preselectConfig) {
configSelect.value = preselectConfig;
}
updateTrackImages();
});
}
function updateTrackImages() {
previewImg.src = "/img/preview/" + trackSelect.value + "/" + configSelect.value;
outlineImg.src = "/img/outline/" + trackSelect.value + "/" + configSelect.value;
}
function apply() {
const URL = '/changetrack/' + trackSelect.value + "/" + configSelect.value;
fetch(URL, { method: "POST" })
.then(res => res.json())
.then(data => {
console.log("server response:" + data)
if (data.status == "success") {
alert("Track changed successfully!");
} else {
alert("Failed to change track: " + data.message);
}
});
}
trackSelect.addEventListener("change", updateTrack);
configSelect.addEventListener("change", updateTrackImages);
if (window.addEventListener) // W3C standard
{
window.addEventListener('load', updateCurrentTrack, false); // NB **not** 'onload'
}
</script>
<script>
function updateGameModes() {
fetch('/gamemodes')
.then(r => r.json())
.then(data => {
document.querySelector(".practice-minutes").value = data.gamemodes.practice_minutes;
document.querySelector(".qualify-minutes").value = data.gamemodes.qualify_minutes;
document.querySelector(".race-laps").value = data.gamemodes.race_laps;
});
}
function applyGamemodes() {
const practiceMinutes = document.querySelector(".practice-minutes").value || 0;
const qualifyMinutes = document.querySelector(".qualify-minutes").value || 0;
const raceLaps = document.querySelector(".race-laps").value || 0;
const payload = {
gamemodes: {
practice_minutes: parseInt(practiceMinutes),
qualify_minutes: parseInt(qualifyMinutes),
race_laps: parseInt(raceLaps)
}
};
fetch("/changegamemodes", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => {
console.log("Server response:", data);
if (data.status == "success") {
alert("Gamemodes changed successfully!");
} else {
alert("Failed to change gamemodes: " + data.message);
}
});
}
if (window.addEventListener) // W3C standard
{
window.addEventListener('load', updateGameModes, false); // NB **not** 'onload'
}
</script>
<script>
const addCarBtn = document.getElementById("addCarBtn");
const carList = document.getElementById("carList");
const applyCarsBtn = document.getElementById("applyCarsBtn");
applyCarsBtn.addEventListener("click", applyCars);
function applyCars() {
const blocks = document.querySelectorAll(".car-block");
let cars = [];
blocks.forEach(block => {
const carSelect = block.querySelector(".car-select");
const skinSelect = block.querySelector(".skin-select");
const amountInput = block.querySelector(".amount-input");
const carName = carSelect?.value || "";
const skinName = skinSelect?.value || "";
let amount = 1;
if (parseInt(amountInput.value) >= 1) {
console.log("setting amount to value:", amountInput.value);
amount = parseInt(amountInput.value);
}
console.log("Adding car:", carName, "skin:", skinName, "amount:", amount);
if (carName) {
for (let i = 0; i < amount; i++) {
cars.push({
car: carName,
skin: skinName
});
}
}
});
const payload = { cars };
console.log("Sending cars:", payload);
fetch("/changecars", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => {
console.log("Server response:", data);
if (data.status == "success") {
alert("Cars changed successfully!");
} else {
alert("Failed to change cars: " + data.message);
}
});
}
async function addCar(preselectedCar = null, preselectedSkin = null, amount = 0) {
const wrapper = document.createElement("div");
wrapper.style.marginTop = "12px";
wrapper.className = "car-block";
const carSelect = document.createElement("select");
carSelect.className = "car-select";
const skinSelect = document.createElement("select");
skinSelect.className = "skin-select";
skinSelect.style.marginLeft = "10px";
const delBtn = document.createElement("button");
delBtn.textContent = "Remove";
delBtn.style.marginLeft = "10px";
delBtn.onclick = () => {
wrapper.classList.add("remove-anim");
setTimeout(() => wrapper.remove(), 350);
};
const copyBtn = document.createElement("button");
copyBtn.textContent = "Copy";
copyBtn.style.marginLeft = "10px";
copyBtn.onclick = () => {
addCar(carSelect.value, skinSelect.value);
};
const amountSelect = document.createElement("input");
amountSelect.style.marginLeft = "10px";
amountSelect.className = "amount-input";
amountSelect.type = "number";
amountSelect.min = "1";
if (amount > 0) {
amountSelect.value = amount;
}
amountSelect.placeholder = "Amount";
amountSelect.min = "10";
const img = document.createElement("img");
img.className = "skin-preview";
img.style.display = "block";
img.style.marginTop = "10px";
img.style.maxWidth = "400px";
wrapper.appendChild(carSelect);
wrapper.appendChild(skinSelect);
wrapper.appendChild(amountSelect);
wrapper.appendChild(copyBtn);
wrapper.appendChild(delBtn);
wrapper.appendChild(img);
carList.appendChild(wrapper);
const url = '/cars';
const res = await fetch(url);
const data = await res.json();
const cars = data.cars;
console.log(cars);
cars.forEach(car => {
let o = document.createElement("option");
o.value = car.name; // of car.id
o.textContent = car.name;
carSelect.appendChild(o);
});
function loadSkins(carName) {
const car = cars.find(c => c.name === carName);
skinSelect.innerHTML = "";
car.skins.forEach(cfg => {
let o = document.createElement("option");
o.value = cfg.name;
o.textContent = cfg.name;
skinSelect.appendChild(o);
});
// show first skin
try {
updateImage(car.skins[0].image);
} catch (e) {
updateImage("");
}
}
function updateImage(imgPath) {
img.src = imgPath;
}
carSelect.addEventListener("change", () => {
const selectedCar = carSelect.value;
loadSkins(selectedCar);
});
skinSelect.addEventListener("change", () => {
const selectedCar = carSelect.value;
const selectedSkin = skinSelect.value;
const car = cars.find(c => c.name === selectedCar);
const cfg = car.skins.find(x => x.name === selectedSkin);
try {
updateImage(cfg.image);
} catch (e) {
updateImage("");
}
});
if (preselectedCar) {
carSelect.value = preselectedCar;
loadSkins(preselectedCar);
if (preselectedSkin) {
skinSelect.value = preselectedSkin;
const car = cars.find(c => c.name === preselectedCar);
const cfg = car.skins.find(x => x.name === preselectedSkin);
updateImage(cfg ? cfg.image : "");
}
} else {
loadSkins(cars[0].name);
}
}
function getCurrentCars() {
const url = '/currentcars';
fetch(url)
.then(res => res.json())
.then(data => {
console.log("Current cars:", data);
data.cars.forEach(c => {
addCar(c.car, c.skin, c.amount);
});
});
}
addCarBtn.addEventListener("click", addCar);
if (window.addEventListener) // W3C standard
{
window.addEventListener('load', getCurrentCars, false); // NB **not** 'onload'
}
</script>
</body>
</html>