193 lines
8.7 KiB
PHP
193 lines
8.7 KiB
PHP
<h1>World</h1>
|
|
<p>Use WASD keys to move the character</p>
|
|
<p>Current location: <span id="char_x"><?= location('x') ?></span>, <span id="char_y"><?= location('y') ?></span></p>
|
|
|
|
<div id="canvas-container">
|
|
<canvas id="canvas"></canvas>
|
|
</div>
|
|
|
|
<script>
|
|
const game = {
|
|
canvas: document.getElementById('canvas'),
|
|
csrf: '<?= csrf() ?>',
|
|
tiles: {
|
|
size: 32,
|
|
img: new Image(),
|
|
cols: 3
|
|
},
|
|
sprites: {
|
|
size: 32,
|
|
img: new Image(),
|
|
cols: 6
|
|
}
|
|
}
|
|
|
|
const map = [
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
]
|
|
|
|
let loc_span = {
|
|
x: document.getElementById('char_x'),
|
|
y: document.getElementById('char_y')
|
|
}
|
|
|
|
let player = { x: <?= location('x') ?>, y: <?= location('y') ?>, char: 23, sprite: { x: 0, y: 0 } }
|
|
let camera = { x: 0, y: 0 }
|
|
let visible = { x: 0, y: 0 }
|
|
|
|
game.tiles.img.src = '/assets/img/world/tiles.jpg';
|
|
game.sprites.img.src = '/assets/img/world/rogues.png';
|
|
|
|
function getPlayerSprite() {
|
|
let col = player.char % game.sprites.cols
|
|
let row = Math.floor(player.char / game.sprites.cols)
|
|
player.sprite = { x: col * game.sprites.size, y: row * game.sprites.size }
|
|
}
|
|
|
|
function updateCanvasSize() {
|
|
game.canvas.width = game.canvas.clientWidth
|
|
game.canvas.height = game.canvas.clientHeight
|
|
visible.x = Math.ceil(game.canvas.width / game.tiles.size)
|
|
visible.y = Math.ceil(game.canvas.height / game.tiles.size)
|
|
updateCamera()
|
|
render()
|
|
}
|
|
|
|
function setupEventListeners() {
|
|
window.addEventListener('resize', updateCanvasSize)
|
|
window.addEventListener('keydown', handleKeyPress)
|
|
}
|
|
|
|
// Handle keyboard input
|
|
function handleKeyPress(e) {
|
|
let moved = false;
|
|
const newPos = { ...player }
|
|
|
|
// 0 = up, 1 = down, 2 = left, 3 = right
|
|
direction = {
|
|
'w': 0,
|
|
's': 1,
|
|
'a': 2,
|
|
'd': 3,
|
|
'ArrowUp': 0,
|
|
'ArrowDown': 1,
|
|
'ArrowLeft': 2,
|
|
'ArrowRight': 3
|
|
}[e.key];
|
|
|
|
// Direction vectors: [up, down, left, right]
|
|
const dx = [0, 0, -1, 1];
|
|
const dy = [-1, 1, 0, 0];
|
|
|
|
// Calculate new position
|
|
const newX = player.x + dx[direction];
|
|
const newY = player.y + dy[direction];
|
|
|
|
if (direction !== undefined) {
|
|
// Check if the new position is outside the map bounds
|
|
if (newX < 0 || newX >= map[0].length || newY < 0 || newY >= map.length) return;
|
|
|
|
if (map[newY][newX] !== 0) return;
|
|
|
|
// Execute a POST request to /move. If successful, the server will return a new x,y position
|
|
fetch('/move', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: `direction=${direction}&csrf=${game.csrf}`
|
|
}).then(response => {
|
|
if (response.ok) {
|
|
response.json().then(data => {
|
|
player.x = data.x
|
|
player.y = data.y
|
|
loc_span.x.textContent = player.x
|
|
loc_span.y.textContent = player.y
|
|
updateCamera()
|
|
render()
|
|
});
|
|
} else {
|
|
throw new Error('Failed to move character');
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Update camera position
|
|
function updateCamera() {
|
|
camera.x = player.x * game.tiles.size - canvas.width / 2;
|
|
camera.y = player.y * game.tiles.size - canvas.height / 2;
|
|
|
|
// Clamp camera to map bounds
|
|
camera.x = Math.max(0, Math.min(camera.x,
|
|
map[0].length * game.tiles.size - canvas.width));
|
|
camera.y = Math.max(0, Math.min(camera.y,
|
|
map.length * game.tiles.size - canvas.height));
|
|
}
|
|
|
|
// Render the game
|
|
function render() {
|
|
const ctx = game.canvas.getContext('2d')
|
|
|
|
ctx.clearRect(0, 0, game.canvas.width, game.canvas.height)
|
|
|
|
// Calculate visible tile range
|
|
const startTileX = Math.floor(camera.x / game.tiles.size)
|
|
const startTileY = Math.floor(camera.y / game.tiles.size)
|
|
const endTileX = startTileX + visible.x + 1
|
|
const endTileY = startTileY + visible.y + 1
|
|
|
|
// Only render visible tiles
|
|
for (let y = startTileY; y < endTileY; y++) {
|
|
if (y >= map.length) continue
|
|
|
|
for (let x = startTileX; x < endTileX; x++) {
|
|
if (x >= map[0].length) continue
|
|
|
|
const screenX = x * game.tiles.size - camera.x
|
|
const screenY = y * game.tiles.size - camera.y
|
|
|
|
ctx.drawImage(game.tiles.img, map[y][x] * game.tiles.size, 0, game.tiles.size, game.tiles.size,
|
|
screenX, screenY, game.tiles.size, game.tiles.size)
|
|
}
|
|
}
|
|
|
|
// Render the player on top of the map using their current position
|
|
ctx.drawImage(game.sprites.img, player.sprite.x, player.sprite.y, game.sprites.size, game.sprites.size,
|
|
(player.x * game.sprites.size) - camera.x, (player.y * game.sprites.size) - camera.y,
|
|
game.sprites.size, game.sprites.size)
|
|
}
|
|
|
|
window.addEventListener('load', () => {
|
|
getPlayerSprite()
|
|
updateCanvasSize()
|
|
setupEventListeners()
|
|
render()
|
|
})
|
|
</script>
|