add inspiration
This commit is contained in:
284
dungeon_crawler_inspiration/ascii_dungeon_crawler.html
Normal file
284
dungeon_crawler_inspiration/ascii_dungeon_crawler.html
Normal file
@@ -0,0 +1,284 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ASCII Dungeon Crawler</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #000;
|
||||
color: #0f0;
|
||||
font-family: "Courier New", monospace;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#gameContainer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#viewport {
|
||||
font-size: 8px;
|
||||
line-height: 8px;
|
||||
white-space: pre;
|
||||
border: 2px solid #0f0;
|
||||
display: inline-block;
|
||||
background-color: #000;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#controls {
|
||||
margin-top: 20px;
|
||||
color: #0f0;
|
||||
}
|
||||
|
||||
#mapInput {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background-color: #001100;
|
||||
color: #0f0;
|
||||
border: 1px solid #0f0;
|
||||
font-family: "Courier New", monospace;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #001100;
|
||||
color: #0f0;
|
||||
border: 1px solid #0f0;
|
||||
padding: 5px 10px;
|
||||
font-family: "Courier New", monospace;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #002200;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="gameContainer">
|
||||
<div id="viewport"></div>
|
||||
<div id="controls">
|
||||
<div>Use WASD or Arrow Keys to move</div>
|
||||
<div>Mouse to look around</div>
|
||||
</div>
|
||||
<div id="mapInput">
|
||||
<div>Load your map (# = walls, space = floor):</div>
|
||||
<br />
|
||||
<textarea id="mapText" rows="10" cols="50">
|
||||
####################
|
||||
# #
|
||||
# ### ### #
|
||||
# # # #
|
||||
# # #### # #
|
||||
# # # #
|
||||
# # # #
|
||||
# # #### # #
|
||||
# # # #
|
||||
# ### ### #
|
||||
# #
|
||||
####################
|
||||
</textarea
|
||||
>
|
||||
<br /><br />
|
||||
<button onclick="loadMap()">Load Map</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
class DungeonCrawler {
|
||||
constructor() {
|
||||
this.viewport = document.getElementById("viewport");
|
||||
this.width = 120;
|
||||
this.height = 40;
|
||||
|
||||
// Player position and orientation
|
||||
this.playerX = 10;
|
||||
this.playerY = 10;
|
||||
this.playerAngle = 0;
|
||||
this.fov = Math.PI / 3; // 60 degrees
|
||||
|
||||
// Map
|
||||
this.map = [];
|
||||
this.mapWidth = 0;
|
||||
this.mapHeight = 0;
|
||||
|
||||
// Raycasting settings
|
||||
this.maxDepth = 20;
|
||||
|
||||
this.setupControls();
|
||||
this.loadDefaultMap();
|
||||
this.gameLoop();
|
||||
}
|
||||
|
||||
loadDefaultMap() {
|
||||
const defaultMap = document.getElementById("mapText").value;
|
||||
this.parseMap(defaultMap);
|
||||
}
|
||||
|
||||
parseMap(mapString) {
|
||||
const lines = mapString.trim().split("\n");
|
||||
this.mapHeight = lines.length;
|
||||
this.mapWidth = Math.max(...lines.map((line) => line.length));
|
||||
|
||||
this.map = [];
|
||||
for (let y = 0; y < this.mapHeight; y++) {
|
||||
this.map[y] = [];
|
||||
const line = lines[y] || "";
|
||||
for (let x = 0; x < this.mapWidth; x++) {
|
||||
this.map[y][x] = line[x] === "#" ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Find a starting position (first open space)
|
||||
for (let y = 1; y < this.mapHeight - 1; y++) {
|
||||
for (let x = 1; x < this.mapWidth - 1; x++) {
|
||||
if (this.map[y][x] === 0) {
|
||||
this.playerX = x + 0.5;
|
||||
this.playerY = y + 0.5;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupControls() {
|
||||
const keys = {};
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
keys[e.key.toLowerCase()] = true;
|
||||
});
|
||||
|
||||
document.addEventListener("keyup", (e) => {
|
||||
keys[e.key.toLowerCase()] = false;
|
||||
});
|
||||
|
||||
// Mouse look
|
||||
document.addEventListener("mousemove", (e) => {
|
||||
this.playerAngle += e.movementX * 0.003;
|
||||
});
|
||||
|
||||
document.addEventListener("click", () => {
|
||||
document.body.requestPointerLock();
|
||||
});
|
||||
|
||||
// Movement
|
||||
setInterval(() => {
|
||||
const moveSpeed = 0.1;
|
||||
const rotSpeed = 0.05;
|
||||
|
||||
if (keys["w"] || keys["arrowup"]) {
|
||||
const newX = this.playerX + Math.cos(this.playerAngle) * moveSpeed;
|
||||
const newY = this.playerY + Math.sin(this.playerAngle) * moveSpeed;
|
||||
if (!this.isWall(newX, newY)) {
|
||||
this.playerX = newX;
|
||||
this.playerY = newY;
|
||||
}
|
||||
}
|
||||
if (keys["s"] || keys["arrowdown"]) {
|
||||
const newX = this.playerX - Math.cos(this.playerAngle) * moveSpeed;
|
||||
const newY = this.playerY - Math.sin(this.playerAngle) * moveSpeed;
|
||||
if (!this.isWall(newX, newY)) {
|
||||
this.playerX = newX;
|
||||
this.playerY = newY;
|
||||
}
|
||||
}
|
||||
if (keys["a"] || keys["arrowleft"]) {
|
||||
this.playerAngle -= rotSpeed;
|
||||
}
|
||||
if (keys["d"] || keys["arrowright"]) {
|
||||
this.playerAngle += rotSpeed;
|
||||
}
|
||||
}, 16);
|
||||
}
|
||||
|
||||
isWall(x, y) {
|
||||
const mapX = Math.floor(x);
|
||||
const mapY = Math.floor(y);
|
||||
|
||||
if (mapX < 0 || mapX >= this.mapWidth || mapY < 0 || mapY >= this.mapHeight) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.map[mapY][mapX] === 1;
|
||||
}
|
||||
|
||||
castRay(angle) {
|
||||
const rayX = Math.cos(angle);
|
||||
const rayY = Math.sin(angle);
|
||||
|
||||
let distance = 0;
|
||||
const step = 0.02;
|
||||
|
||||
while (distance < this.maxDepth) {
|
||||
const testX = this.playerX + rayX * distance;
|
||||
const testY = this.playerY + rayY * distance;
|
||||
|
||||
if (this.isWall(testX, testY)) {
|
||||
return distance;
|
||||
}
|
||||
|
||||
distance += step;
|
||||
}
|
||||
|
||||
return this.maxDepth;
|
||||
}
|
||||
|
||||
render() {
|
||||
let screen = "";
|
||||
|
||||
for (let y = 0; y < this.height; y++) {
|
||||
for (let x = 0; x < this.width; x++) {
|
||||
const rayAngle = this.playerAngle - this.fov / 2 + (x / this.width) * this.fov;
|
||||
const distance = this.castRay(rayAngle);
|
||||
|
||||
// Calculate wall height
|
||||
const wallHeight = this.height / 2 / distance;
|
||||
const wallTop = this.height / 2 - wallHeight;
|
||||
const wallBottom = this.height / 2 + wallHeight;
|
||||
|
||||
let char = " ";
|
||||
|
||||
if (y < wallTop) {
|
||||
// Ceiling
|
||||
char = " ";
|
||||
} else if (y > wallBottom) {
|
||||
// Floor
|
||||
char = ".";
|
||||
} else {
|
||||
// Wall
|
||||
if (distance < 2) char = "█";
|
||||
else if (distance < 4) char = "▓";
|
||||
else if (distance < 8) char = "▒";
|
||||
else if (distance < 12) char = "░";
|
||||
else char = "·";
|
||||
}
|
||||
|
||||
screen += char;
|
||||
}
|
||||
screen += "\n";
|
||||
}
|
||||
|
||||
this.viewport.textContent = screen;
|
||||
}
|
||||
|
||||
gameLoop() {
|
||||
this.render();
|
||||
requestAnimationFrame(() => this.gameLoop());
|
||||
}
|
||||
}
|
||||
|
||||
function loadMap() {
|
||||
game.parseMap(document.getElementById("mapText").value);
|
||||
}
|
||||
|
||||
// Start the game
|
||||
const game = new DungeonCrawler();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user