| Server IP : 172.67.187.206 / Your IP : 172.71.28.156 Web Server : Apache/2.4.25 (Win32) OpenSSL/1.0.2j PHP/5.6.30 System : Windows NT WIN-ECQAAA40806 6.2 build 9200 (Windows Server 2012 Standard Edition) i586 User : SYSTEM ( 0) PHP Version : 5.6.30 Disable Function : NONE MySQL : ON | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /Inetpub/www/game/ |
Upload File : |
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>เกมสร้างรถไฟ</title>
<style>
body {
margin: 0;
overflow: hidden;
font-family: 'Arial', sans-serif;
background-color: #f0f8ff; /* Light blue */
display: flex;
flex-direction: column;
height: 100vh;
touch-action: manipulation; /* Prevent default touch actions */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#palette {
background-color: #e0e0e0;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
gap: 15px; /* Adjust gap for more items */
border-bottom: 2px solid #ccc;
min-height: 120px;
overflow-x: auto; /* Allow scrolling on palette if too many items */
white-space: nowrap;
}
.train-part-selection {
width: 90px; /* Slightly smaller for more items */
height: 70px;
background-color: #f9f9f9;
border: 2px solid #333;
border-radius: 8px;
cursor: grab; /* Indicate draggable */
display: inline-flex; /* Use inline-flex for horizontal scroll */
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 11px; /* Smaller font */
font-weight: bold;
color: #555;
transition: transform 0.2s, box-shadow 0.2s;
overflow: hidden;
flex-shrink: 0; /* Prevent shrinking */
position: relative; /* For draggable styling */
}
.train-part-selection:hover {
transform: translateY(-3px); /* Smaller hover effect */
box-shadow: 0 4px 12px rgba(0,0,0,0.2); /* Smaller shadow */
}
.train-part-selection img {
max-width: 80%; /* Smaller image */
max-height: 80%;
object-fit: contain;
margin-bottom: 3px;
}
/* Dragging feedback */
.dragging {
opacity: 0.7;
border-style: dashed;
box-shadow: 0 0 15px rgba(0, 0, 255, 0.5);
z-index: 1000; /* Ensure it's on top */
}
#game-options {
background-color: #f0f0f0;
padding: 10px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
font-size: 0.9em;
color: #333;
}
#game-options select, #game-options input[type="number"] {
padding: 5px 10px;
border-radius: 5px;
border: 1px solid #ccc;
font-size: 0.9em;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
#game-options label {
white-space: nowrap;
}
#building-area-container {
flex-grow: 1;
overflow: hidden;
position: relative;
background-color: #c8e6c9; /* Light green (grass) */
border-top: 5px dashed #8bc34a; /* Top rail */
border-bottom: 5px dashed #8bc34a; /* Bottom rail */
/* Parallax Background */
background-image: url('https://www.transparenttextures.com/patterns/clean-textile.png'), url('https://picsum.photos/800/600?random=1'); /* Example textures/image */
background-repeat: repeat, no-repeat;
background-size: auto, cover; /* Tile texture, cover image */
background-position: 0 0, center center;
}
#building-area {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
white-space: nowrap;
display: flex;
align-items: flex-end;
height: 100%;
}
.train-part {
display: inline-block;
width: 100px;
height: 80px;
background-color: #b3e5fc;
border: 2px solid #333;
border-left: none;
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column; /* For delete button */
justify-content: center;
align-items: center;
font-size: 12px;
color: #333;
flex-shrink: 0;
touch-action: none; /* Disable default touch actions like scroll/zoom for parts */
}
.train-part:first-child {
border-left: 2px solid #333;
}
.train-part img {
max-width: 90%;
max-height: 90%;
object-fit: contain;
}
/* Wheels */
.train-part::before, .train-part::after {
content: '';
position: absolute;
bottom: -15px;
width: 20px;
height: 20px;
background-color: #555;
border-radius: 50%;
border: 2px solid #222;
}
.train-part::before { left: 10px; }
.train-part::after { right: 10px; }
.locomotive { background-color: #ef9a9a; }
.carriage { background-color: #b2dfdb; }
.cargo { background-color: #ffcc80; }
.passenger-blue { background-color: #81d4fa; } /* New color */
.boxcar { background-color: #bcaaa4; } /* New color */
/* Delete Button on Train Part */
.delete-part-btn {
position: absolute;
top: -5px; /* Adjust as needed */
right: -5px; /* Adjust as needed */
background-color: #f44336;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: bold;
cursor: pointer;
z-index: 10;
line-height: 1;
padding: 0;
border: 1px solid #a00;
opacity: 0.8;
display: none; /* Hide by default, show when editing */
}
.delete-part-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.editable .delete-part-btn {
display: flex; /* Show delete button when in editable mode */
}
#controls {
background-color: #e0e0e0;
padding: 15px;
display: flex;
justify-content: center;
gap: 20px;
border-top: 2px solid #ccc;
}
.control-btn {
padding: 10px 25px;
font-size: 1.2em;
font-weight: bold;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s, transform 0.2s;
}
#run-btn { background-color: #4CAF50; color: white; }
#run-btn:hover { background-color: #45a049; transform: translateY(-2px); }
#stop-btn { background-color: #FFC107; color: #333; }
#stop-btn:hover { background-color: #ffb300; transform: translateY(-2px); }
#clear-btn { background-color: #F44336; color: white; }
#clear-btn:hover { background-color: #da190b; transform: translateY(-2px); }
</style>
</head>
<body>
<div id="palette">
<div class="train-part-selection" data-type="locomotive" data-img="https://www.flaticon.com/svg/static/icons/svg/3081/3081977.svg">
<img src="https://www.flaticon.com/svg/static/icons/svg/3081/3081977.svg" alt="หัวรถจักร"> หัวรถจักร
</div>
<div class="train-part-selection" data-type="carriage" data-img="https://www.flaticon.com/svg/static/icons/svg/3081/3081978.svg">
<img src="https://www.flaticon.com/svg/static/icons/svg/3081/3081978.svg" alt="ตู้โดยสาร"> ตู้โดยสาร
</div>
<div class="train-part-selection" data-type="cargo" data-img="https://www.flaticon.com/svg/static/icons/svg/3081/3081979.svg">
<img src="https://www.flaticon.com/svg/static/icons/svg/3081/3081979.svg" alt="ตู้สินค้า"> ตู้สินค้า
</div>
<div class="train-part-selection" data-type="passenger-blue" data-img="https://www.flaticon.com/svg/static/icons/svg/3081/3081980.svg">
<img src="https://www.flaticon.com/svg/static/icons/svg/3081/3081980.svg" alt="ตู้โดยสารสีฟ้า"> ตู้โดยสารฟ้า
</div>
<div class="train-part-selection" data-type="boxcar" data-img="https://www.flaticon.com/svg/static/icons/svg/3081/3081981.svg">
<img src="https://www.flaticon.com/svg/static/icons/svg/3081/3081981.svg" alt="ตู้บรรทุก"> ตู้บรรทุก
</div>
</div>
<div id="game-options">
<label for="train-speed-select">ความเร็วรถไฟ:</label>
<select id="train-speed-select">
<option value="1">ช้า</option>
<option value="2" selected>ปานกลาง</option>
<option value="4">เร็ว</option>
<option value="8">เร็วมาก</option>
</select>
<label for="bg-speed-input">ความเร็วฉากหลัง:</label>
<input type="number" id="bg-speed-input" value="1" min="0.1" step="0.1">
</div>
<div id="building-area-container">
<div id="building-area">
</div>
</div>
<div id="controls">
<button id="run-btn" class="control-btn">วิ่ง!</button>
<button id="stop-btn" class="control-btn">หยุด/แก้ไข</button>
<button id="clear-btn" class="control-btn">ล้างรถไฟ</button>
</div>
<audio id="add-sound" src="https://www.soundjay.com/buttons/beep-07.wav" preload="auto"></audio>
<audio id="delete-sound" src="https://www.soundjay.com/buttons/beep-06.wav" preload="auto"></audio>
<audio id="train-run-sound" src="https://www.soundjay.com/transport/train-crossing-bell-01.wav" loop preload="auto"></audio>
<script>
const palette = document.getElementById('palette');
const buildingArea = document.getElementById('building-area');
const runBtn = document.getElementById('run-btn');
const stopBtn = document.getElementById('stop-btn');
const clearBtn = document.getElementById('clear-btn');
const trainSpeedSelect = document.getElementById('train-speed-select'); // New speed select
const bgSpeedInput = document.getElementById('bg-speed-input'); // New background speed input
// Audio elements
const addSound = document.getElementById('add-sound');
const deleteSound = document.getElementById('delete-sound');
const trainRunSound = document.getElementById('train-run-sound');
let isRunning = false;
let animationFrameId;
let trainSpeed = parseFloat(trainSpeedSelect.value); // Initial train speed from select
let backgroundScrollPos = 0;
let backgroundSpeed = parseFloat(bgSpeedInput.value); // Initial background speed
let currentDraggedPart = null; // For drag and drop
// --- Event Listeners for Game Options ---
trainSpeedSelect.addEventListener('change', (event) => {
trainSpeed = parseFloat(event.target.value);
});
bgSpeedInput.addEventListener('change', (event) => {
backgroundSpeed = parseFloat(event.target.value);
});
// --- เพิ่มชิ้นส่วนรถไฟเมื่อคลิก/Drag & Drop ---
// Drag start for palette items
palette.addEventListener('pointerdown', (e) => {
if (e.target.closest('.train-part-selection') && !isRunning) {
e.preventDefault(); // Prevent default browser drag behavior
currentDraggedPart = e.target.closest('.train-part-selection').cloneNode(true);
currentDraggedPart.classList.add('dragging');
currentDraggedPart.style.position = 'absolute';
currentDraggedPart.style.zIndex = '1000';
currentDraggedPart.style.pointerEvents = 'none'; // Don't block mouse events
document.body.appendChild(currentDraggedPart);
const initialX = e.clientX;
const initialY = e.clientY;
const offsetX = e.clientX - e.target.closest('.train-part-selection').getBoundingClientRect().left;
const offsetY = e.clientY - e.target.closest('.train-part-selection').getBoundingClientRect().top;
const moveAt = (pageX, pageY) => {
currentDraggedPart.style.left = (pageX - offsetX) + 'px';
currentDraggedPart.style.top = (pageY - offsetY) + 'px';
};
moveAt(e.pageX, e.pageY);
const onMouseMove = (event) => {
moveAt(event.pageX, event.pageY);
};
document.addEventListener('pointermove', onMouseMove);
currentDraggedPart.onpointerup = () => {
document.removeEventListener('pointermove', onMouseMove);
currentDraggedPart.onpointerup = null;
// Check if dropped over building area
const buildingAreaRect = buildingArea.getBoundingClientRect();
const draggedRect = currentDraggedPart.getBoundingClientRect();
if (
draggedRect.right > buildingAreaRect.left &&
draggedRect.left < buildingAreaRect.right &&
draggedRect.bottom > buildingAreaRect.top &&
draggedRect.top < buildingAreaRect.bottom
) {
// Determine where to add the part (always at the end for simplicity)
const partType = currentDraggedPart.dataset.type;
addTrainPart(partType, currentDraggedPart.dataset.img);
}
currentDraggedPart.remove();
currentDraggedPart = null;
};
}
});
// Fallback for click (if not dragging) or direct click
palette.addEventListener('click', (event) => {
if (isRunning || currentDraggedPart) return; // Don't add if running or dragging
const target = event.target.closest('.train-part-selection');
if (!target) return;
const partType = target.dataset.type;
const partImg = target.dataset.img;
addTrainPart(partType, partImg);
});
function addTrainPart(type, img_src) {
if (isRunning) return; // Double check
const part = document.createElement('div');
part.classList.add('train-part', type);
part.dataset.type = type;
const img = document.createElement('img');
img.src = img_src;
img.alt = type;
part.appendChild(img);
// Delete button
const deleteBtn = document.createElement('button');
deleteBtn.classList.add('delete-part-btn');
deleteBtn.textContent = 'X';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent event from bubbling to parent train-part
if (!isRunning) {
part.remove();
deleteSound.play();
adjustBuildingAreaPosition(); // Adjust position after deletion
}
});
part.appendChild(deleteBtn);
// Validation rules
if (type === 'locomotive') {
if (buildingArea.querySelector('.locomotive')) {
alert('มีหัวรถจักรอยู่แล้ว! รถไฟมีหัวเดียวก็พอจ้า');
return;
}
// If adding locomotive and there are other parts, move them after locomotive
if (buildingArea.children.length > 0) {
// Find the first non-locomotive part and insert before it
const firstNonLocomotive = buildingArea.querySelector('.train-part:not(.locomotive)');
if (firstNonLocomotive) {
buildingArea.insertBefore(part, firstNonLocomotive);
} else {
buildingArea.appendChild(part);
}
} else {
buildingArea.appendChild(part);
}
} else {
if (!buildingArea.querySelector('.locomotive')) {
alert('ต้องมีหัวรถจักรก่อนนะจ๊ะ!');
return;
}
buildingArea.appendChild(part);
}
addSound.play();
adjustBuildingAreaPosition();
}
// Adjust building area position to keep train visible
function adjustBuildingAreaPosition() {
const trainWidth = buildingArea.scrollWidth;
const containerWidth = buildingArea.parentElement.offsetWidth;
let currentLeft = parseFloat(buildingArea.style.left || 0);
// If the train is too long and goes off the right, pull it left
if (currentLeft + trainWidth < containerWidth) {
buildingArea.style.left = (containerWidth - trainWidth) + 'px';
}
// If it's too short and too far left, bring it back to 0
if (currentLeft > 0) {
buildingArea.style.left = '0px';
}
// Ensure it doesn't go too far left if not running
if (!isRunning && currentLeft < -(trainWidth - containerWidth) && trainWidth > containerWidth) {
buildingArea.style.left = -(trainWidth - containerWidth) + 'px';
}
}
// --- การเคลื่อนที่ของรถไฟและฉากหลัง ---
function animateTrainAndBackground() {
if (isRunning) {
let currentLeft = parseFloat(buildingArea.style.left || 0);
buildingArea.style.left = (currentLeft - trainSpeed) + 'px';
// Loop the train if it goes off screen
if (currentLeft < -buildingArea.scrollWidth) {
buildingArea.style.left = window.innerWidth + 'px'; // Reset to right of screen
}
// Animate background
backgroundScrollPos -= backgroundSpeed;
if (backgroundScrollPos <= -buildingArea.parentElement.offsetWidth) {
backgroundScrollPos = 0; // Reset background position
}
buildingArea.parentElement.style.backgroundPosition = `${backgroundScrollPos}px 0, center center`;
animationFrameId = requestAnimationFrame(animateTrainAndBackground);
}
}
function runTrain() {
if (buildingArea.children.length === 0) {
alert('ยังไม่มีรถไฟเลย! สร้างก่อนนะ');
return;
}
if (!buildingArea.querySelector('.locomotive')) {
alert('รถไฟต้องมีหัวรถจักรนะจ๊ะ!');
return;
}
isRunning = true;
runBtn.disabled = true;
stopBtn.disabled = false;
clearBtn.disabled = true;
palette.style.pointerEvents = 'none'; // Disable palette interaction
trainRunSound.play(); // Play train sound
// Hide delete buttons
document.querySelectorAll('.train-part').forEach(part => part.classList.remove('editable'));
// Ensure train starts from a reasonable position if already off-screen
const currentLeft = parseFloat(buildingArea.style.left || 0);
if (currentLeft + buildingArea.scrollWidth < 0) {
buildingArea.style.left = window.innerWidth + 'px';
}
animateTrainAndBackground();
}
function stopTrain() {
isRunning = false;
runBtn.disabled = false;
stopBtn.disabled = true;
clearBtn.disabled = false;
palette.style.pointerEvents = 'auto'; // Enable palette interaction
trainRunSound.pause(); // Pause train sound
trainRunSound.currentTime = 0; // Reset sound
// Show delete buttons
document.querySelectorAll('.train-part').forEach(part => part.classList.add('editable'));
cancelAnimationFrame(animationFrameId);
}
// --- ปุ่ม "ล้างรถไฟ" ---
clearBtn.addEventListener('click', () => {
if (confirm('คุณต้องการล้างรถไฟทั้งหมดใช่หรือไม่?')) {
buildingArea.innerHTML = ''; // Remove all parts
buildingArea.style.left = '0px'; // Reset position
stopTrain(); // Ensure stopped
}
});
// --- Event Listeners for Control Buttons ---
runBtn.addEventListener('click', runTrain);
stopBtn.addEventListener('click', stopTrain);
// Initial setup
document.addEventListener('DOMContentLoaded', () => {
stopTrain(); // Start in stopped/editable mode
buildingArea.style.left = '0px'; // Initial position
});
</script>
</body>
</html>