IMAGE DESCRIPTION:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>gd</title>
<meta name="description" content="yuh">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
background: #111;
overflow: hidden;
}
canvas {
display: block;
margin: auto;
background: #000099;
}
</style>
</head>
<body>
<canvas id="game"></canvas>
<script>
/* Canvas Setup */
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
window.addEventListener("resize", () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
/* Global Variables */
const TILE_SIZE = 40;
const SPIKE_HITBOX_SIZE = TILE_SIZE / 2;
let cameraX = 0;
let paused = false;
let input = { pressed: false };
let wasPressed = false;
let jumpBuffered = false;
let activatedPads = new Set();
let activatedOrbs = new Set();
let activatedPortals = new Set();
let levelCompleted = false;
let player = {
x: 0,
y: 0,
prevX: 0,
width: TILE_SIZE,
height: TILE_SIZE,
velY: 0,
gravity: 0.6,
gravityState: 1, // 1 = normal, -1 = inverted
jumpForce: -10.5,
grounded: false,
speed: 5.5,
rotation: 0,
rotationSpeed: Math.PI / 30,
gamemode: "cube"
};
/* Level Data */
const TILE_TYPES = {
"#": { solid: true },
"A": { hazard: true },
"^": { hazard: true },
"V": { hazard: true },
"v": { hazard: true },
"X": { hazard: true },
"y": { orb: true },
"m": { orb: true },
"r": { orb: true },
"b": { orb: true },
"Y": { pad: true },
"M": { pad: true },
"R": { pad: true },
"B": { pad: true },
")": { portal: true, gravity: -1 },
"(": { portal: true, gravity: 1 },
"]": { portal: true, gamemode: "cube" },
"[": { portal: true, gamemode: "ship" },
"}": { portal: true, gamemode: "ball" },
"{": { portal: true, gamemode: "ufo" },
"E": { levelEnd: true }
};
const levelData = {
level1: [
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#........#...................",
"..............................#...........#.................#..........#............................",
"..............................#...........#............................#............................",
"..............................#############............................#............................",
"................................................................................#...................",
"........................}...........................{.......#...................#...................",
"......................#...........^.......y....y.....Y......#...................#...................",
"..................#...#..##########..................#......#..........#........#...................",
"*......^A.....#...#...#..#........#AAAAAAAAAAAAAAAAAA#......#..........#........#...................",
"###############XXX#XXX##############################################################################",
"####################################################################################################"
]
};
const music = new Audio("SuperUltraMDK.mp3");
music.loop = true;
music.volume = 0.5;
/* Level Helpers */
function getTile(col, row) {
if (
row < 0 ||
row >= levelData.level1.length ||
col < 0 ||
col >= levelData.level1[0].length
) return null;
return levelData.level1[row][col];
}
function tileKey(col, row) {
return col + "," + row;
}
function findStart() {
for (let row = 0; row < levelData.level1.length; row++) {
for (let col = 0; col < levelData.level1[row].length; col++) {
if (levelData.level1[row][col] === "*") {
player.x = col * TILE_SIZE;
player.y = row * TILE_SIZE;
}
}
}
}
function isSolid(tile) {
return TILE_TYPES[tile]?.solid;
}
function isHazard(tile) {
return TILE_TYPES[tile]?.hazard;
}
function isOrb(tile) {
return TILE_TYPES[tile]?.orb;
}
function isPad(tile) {
return TILE_TYPES[tile]?.pad;
}
function isPortal(tile) {
return TILE_TYPES[tile]?.portal;
}
function isLevelEnd(tile) {
return TILE_TYPES[tile]?.levelEnd;
}
/* Input Handling */
function setupInput() {
window.addEventListener("keydown", (e) => {
if (e.code === "Space" || e.code === "ArrowUp") {
input.pressed = true;
}
});
window.addEventListener("keyup", (e) => {
if (e.code === "Space" || e.code === "ArrowUp") {
input.pressed = false;
}
});
window.addEventListener("mousedown", () => input.pressed = true);
window.addEventListener("mouseup", () => input.pressed = false);
window.addEventListener("touchstart", (e) => {
e.preventDefault();
input.pressed = true;
}, { passive: false });
window.addEventListener("touchend", () => input.pressed = false);
window.addEventListener("keydown", (e) => {
if (e.code === "KeyP" || e.code === "Escape") {
togglePause();
}
if (e.code === "Space" || e.code === "ArrowUp") {
input.pressed = true;
music.play().catch(() => { });
}
});
}
/* Physics and Movement */
function applyPhysics() {
const justPressed = input.pressed && !wasPressed;
wasPressed = input.pressed;
if (justPressed && !player.grounded) {
jumpBuffered = true;
}
if (player.gamemode === "cube") {
if (player.grounded && input.pressed) {
player.velY = player.jumpForce * player.gravityState;
jumpBuffered = false;
}
} else if (player.gamemode === "ball") {
if (justPressed && player.grounded) {
player.gravityState *= -1;
player.velY = 0;
}
} else if (player.gamemode === "ufo") {
if (justPressed) {
player.velY = player.jumpForce * player.gravityState;
jumpBuffered = false;
player.grounded = false;
}
}
player.prevX = player.x;
player.x += player.speed;
player.velY += player.gravity * player.gravityState;
player.y += player.velY;
player.grounded = false;
if (player.gamemode === "cube") {
if (!player.grounded) {
player.rotation += player.rotationSpeed * player.gravityState;
}
} else if (player.gamemode === "ball") {
player.rotation += (player.speed / TILE_SIZE) * 2 * Math.PI * player.gravityState;
} else if (player.gamemode === "ufo") {
player.rotation = 0;
}
}
/* Collision Handling */
function handleCollisions() {
const leftCol = Math.floor(player.x / TILE_SIZE);
const rightCol = Math.floor((player.x + player.width - 1) / TILE_SIZE);
const topRow = Math.floor(player.y / TILE_SIZE);
const bottomRow = Math.floor((player.y + player.height) / TILE_SIZE);
for (let row = topRow; row <= bottomRow; row++) {
for (let col = leftCol; col <= rightCol; col++) {
const tile = getTile(col, row);
if (!tile) continue;
const tileX = col * TILE_SIZE;
const tileY = row * TILE_SIZE;
if (isSolid(tile)) {
if (player.gravityState === 1) {
// landing on floor
if (player.velY > 0 && player.y + player.height > tileY && player.y < tileY) {
player.y = tileY - player.height;
player.velY = 0;
player.grounded = true;
if (player.gamemode === "cube") {
player.rotation = Math.round(player.rotation / (Math.PI / 2)) * (Math.PI / 2);
}
}
// hitting ceiling
else if (player.velY < 0 && player.y < tileY + TILE_SIZE) {
player.y = tileY + TILE_SIZE;
player.velY = 0;
}
} else {
// landing on ceiling
if (player.velY < 0 && player.y < tileY + TILE_SIZE && player.y + player.height > tileY + TILE_SIZE) {
player.y = tileY + TILE_SIZE;
player.velY = 0;
player.grounded = true;
if (player.gamemode === "cube") {
player.rotation = Math.round(player.rotation / (Math.PI / 2)) * (Math.PI / 2);
}
}
// hitting floor
else if (player.velY > 0 && player.y + player.height > tileY) {
player.y = tileY - player.height;
player.velY = 0;
}
}
}
if (isHazard(tile) && checkSpikeCollision(tileX, tileY)) {
resetPlayer();
console.log("Hit hazard at (" + col + ", " + row + ")");
}
if (isPad(tile)) {
const key = tileKey(col, row);
const padHeight = TILE_SIZE / 3;
const padX = tileX;
const padY = tileY + TILE_SIZE - padHeight;
const touching =
player.x < padX + TILE_SIZE &&
player.x + player.width > padX &&
player.y + player.height > padY &&
player.y < padY + padHeight;
if (touching && !activatedPads.has(key)) {
activatedPads.add(key);
if (tile === "Y") {
player.velY = -15 * player.gravityState;
} else if (tile === "M") {
player.velY = -10 * player.gravityState;
} else if (tile === "R") {
player.velY = -20 * player.gravityState;
} else if (tile === "B") {
player.velY = -5 * player.gravityState;
player.gravityState *= -1;
}
player.grounded = false;
return;
}
if (!touching) {
activatedPads.delete(key);
}
}
if (isOrb(tile)) {
const key = tileKey(col, row);
if (isCollidingWithTile(tileX, tileY, TILE_SIZE)) {
if ((input.pressed || jumpBuffered) && !activatedOrbs.has(key)) {
activatedOrbs.add(key);
if (tile === "y") {
player.velY = player.jumpForce * player.gravityState;
} else if (tile === "m") {
player.velY = player.jumpForce * player.gravityState + 5;
} else if (tile === "r") {
player.velY = player.jumpForce * player.gravityState - 5;
} else if (tile === "b") {
player.velY = -5 * player.gravityState;
player.gravityState *= -1;
}
jumpBuffered = false;
}
} else {
activatedOrbs.delete(key);
}
}
if (isPortal(tile)) {
const key = tileKey(col, row);
if (isCollidingWithTile(tileX, tileY, TILE_SIZE)) {
if (!activatedPortals.has(key)) {
activatedPortals.add(key);
const portal = TILE_TYPES[tile];
if (portal.gravity !== undefined) {
player.gravityState = portal.gravity;
}
if (portal.gamemode) {
player.gamemode = portal.gamemode;
}
}
} else {
activatedPortals.delete(key);
}
}
if (isLevelEnd(tile)) {
const key = tileKey(col, row);
if (isCollidingWithTile(tileX, tileY, TILE_SIZE)) {
if ((input.pressed || jumpBuffered) && !activatedOrbs.has(key)) {
jumpBuffered = false;
completeLevel();
}
} else {
activatedOrbs.delete(key);
}
}
}
}
}
function checkSpikeCollision(tileX, tileY) {
const hitboxX = tileX + (TILE_SIZE - SPIKE_HITBOX_SIZE) / 2;
const hitboxY = tileY + (TILE_SIZE - SPIKE_HITBOX_SIZE) / 2;
return player.x < hitboxX + SPIKE_HITBOX_SIZE &&
player.x + player.width > hitboxX &&
player.y < hitboxY + SPIKE_HITBOX_SIZE &&
player.y + player.height > hitboxY;
}
function isCollidingWithTile(tileX, tileY, tileSize) {
return player.x < tileX + tileSize &&
player.x + player.width > tileX &&
player.y < tileY + tileSize &&
player.y + player.height > tileY;
}
function checkBlockCrush() {
const col = Math.floor((player.x + player.width / 2) / TILE_SIZE);
const row = Math.floor((player.y + player.height / 2) / TILE_SIZE);
const tile = getTile(col, row);
if (isSolid(tile)) {
resetPlayer();
console.log("Crushed inside block at (" + col + ", " + row + ")");
}
}
/* Camera, Progress Bar, & Rendering*/
function updateCamera() {
const targetX = player.x - canvas.width / 4;
if (targetX > cameraX) {
cameraX += (targetX - cameraX) * 0.1;
}
}
function getProgress() {
const levelWidth = levelData.level1[0].length * TILE_SIZE;
return Math.min(player.x / levelWidth, 1);
}
function drawProgressBar() {
const progress = getProgress();
const barWidth = canvas.width * 0.6;
const barHeight = 10;
const x = (canvas.width - barWidth) / 2;
const y = 20;
// Background
ctx.fillStyle = "rgba(255,255,255,0.2)";
ctx.fillRect(x, y, barWidth, barHeight);
// Fill
ctx.fillStyle = "lime";
ctx.fillRect(x, y, barWidth * progress, barHeight);
// Outline
ctx.strokeStyle = "white";
ctx.lineWidth = 2;
ctx.strokeRect(x, y, barWidth, barHeight);
ctx.fillStyle = "white";
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.fillText(Math.floor(progress * 100) + "%", canvas.width / 2, y + 25);
}
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawProgressBar();
drawLevel();
drawPlayer();
if (levelCompleted) {
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "lime";
ctx.font = "60px Arial";
ctx.textAlign = "center";
ctx.fillText("Level Complete!", canvas.width / 2, canvas.height / 2);
}
if (paused && !levelCompleted) {
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "white";
ctx.font = "48px Arial";
ctx.textAlign = "center";
ctx.fillText("Game Paused", canvas.width / 2, canvas.height / 2);
}
}
/* Drawing Functions */
function drawLevel() {
const startCol = Math.floor(cameraX / TILE_SIZE);
const endCol = startCol + Math.ceil(canvas.width / TILE_SIZE) + 1;
for (let row = 0; row < levelData.level1.length; row++) {
for (let col = startCol; col < endCol; col++) {
const tile = getTile(col, row);
if (!tile) continue;
const x = col * TILE_SIZE - cameraX;
const y = row * TILE_SIZE;
if (isSolid(tile)) {
drawSolid(tile, x, y);
}
if (isHazard(tile)) {
drawHazard(tile, x, y);
}
if (isOrb(tile)) {
drawOrb(tile, x, y);
}
if (isPad(tile)) {
drawPad(tile, x, y);
}
if (isPortal(tile)) {
drawPortal(tile, x, y);
}
if (isLevelEnd(tile)) {
drawEnd(tile, x, y);
}
}
}
}
function drawPlayer() {
const drawX = player.x - cameraX + player.width / 2;
const drawY = player.y + player.height / 2;
ctx.save();
ctx.translate(drawX, drawY);
if (player.gamemode === "cube") {
ctx.rotate(player.rotation);
ctx.fillStyle = "lime";
ctx.fillRect(-player.width / 2, -player.height / 2, player.width, player.height);
} else if (player.gamemode === "ball") {
ctx.rotate(player.rotation);
ctx.fillStyle = "red";
ctx.beginPath();
const size = TILE_SIZE / 2;
ctx.moveTo(0, size);
ctx.lineTo(0, size / 2);
ctx.lineTo(size / 1.5, size / 1.5);
ctx.lineTo(size / 3, size / 3);
ctx.lineTo(size, 0);
ctx.lineTo(size / 2, 0);
ctx.lineTo(size / 1.5, -size / 1.5);
ctx.lineTo(size / 3, -size / 3);
ctx.lineTo(0, -size);
ctx.lineTo(0, -size / 2);
ctx.lineTo(-size / 1.5, -size / 1.5);
ctx.lineTo(-size / 3, -size / 3);
ctx.lineTo(-size, 0);
ctx.lineTo(-size / 2, 0);
ctx.lineTo(-size / 1.5, size / 1.5);
ctx.lineTo(-size / 3, size / 3);
ctx.closePath();
ctx.fill();
} else if (player.gamemode === "ufo") {
ctx.fillStyle = "orange";
// simple UFO shape
ctx.beginPath();
ctx.ellipse(0, 0, TILE_SIZE / 2, TILE_SIZE / 3, 0, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(0, -TILE_SIZE / 6, TILE_SIZE / 6, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
function drawSolid(tile, x, y) {
ctx.save();
ctx.fillStyle = "grey";
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);
ctx.strokeRect(x, y, TILE_SIZE, TILE_SIZE);
ctx.restore();
}
function drawHazard(tile, x, y) {
ctx.save();
ctx.fillStyle = "red";
ctx.beginPath();
if (tile === "A") {
// Full spike pointing up
ctx.moveTo(x, y + TILE_SIZE);
ctx.lineTo(x + TILE_SIZE / 2, y);
ctx.lineTo(x + TILE_SIZE, y + TILE_SIZE);
} else if (tile === "^") {
// Half spike pointing up
ctx.moveTo(x, y + TILE_SIZE);
ctx.lineTo(x + TILE_SIZE / 2, y + TILE_SIZE / 2);
ctx.lineTo(x + TILE_SIZE, y + TILE_SIZE);
} else if (tile === "V") {
// Full spike pointing down
ctx.moveTo(x, y);
ctx.lineTo(x + TILE_SIZE / 2, y + TILE_SIZE);
ctx.lineTo(x + TILE_SIZE, y);
} else if (tile === "v") {
// Half spike pointing down
ctx.moveTo(x, y);
ctx.lineTo(x + TILE_SIZE / 2, y + TILE_SIZE / 2);
ctx.lineTo(x + TILE_SIZE, y);
} else if (tile === "X") {
ctx.moveTo(x, y + TILE_SIZE);
ctx.lineTo(x + TILE_SIZE, y + TILE_SIZE);
ctx.lineTo(x + TILE_SIZE, y);
ctx.lineTo(x, y);
}
ctx.fill();
ctx.restore();
}
function drawOrb(tile, x, y) {
ctx.save();
if (tile === "y") {
ctx.fillStyle = "yellow";
} else if (tile === "m") {
ctx.fillStyle = "magenta";
} else if (tile === "r") {
ctx.fillStyle = "red";
} else if (tile === "b") {
ctx.fillStyle = "cyan"
}
ctx.beginPath();
ctx.arc(x + TILE_SIZE / 2, y + TILE_SIZE / 2, TILE_SIZE / 3, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.strokeStyle = "white";
ctx.lineWidth = 3;
ctx.arc(x + TILE_SIZE / 2, y + TILE_SIZE / 2, TILE_SIZE / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.restore();
}
function drawPad(tile, x, y) {
ctx.save();
if (tile === "Y") {
ctx.fillStyle = "yellow"
} else if (tile === "M") {
ctx.fillStyle = "magenta"
} else if (tile === "R") {
ctx.fillStyle = "red"
} else if (tile === "B") {
ctx.fillStyle = "cyan"
}
const padHeight = TILE_SIZE / 3;
ctx.fillRect(x, y + TILE_SIZE - padHeight, TILE_SIZE, padHeight);
ctx.restore();
}
function drawPortal(tile, x, y) {
ctx.save();
let color = "";
if (tile === ")") {
color = "yellow";
} else if (tile === "(") {
color = "blue";
} else if (tile === "]") {
color = "lime";
} else if (tile === "[") {
color = "magenta";
} else if (tile === "}") {
color = "red";
} else if (tile === "{") {
color = "orange";
}
// Glow
const gradient = ctx.createRadialGradient(
x + TILE_SIZE / 2,
y,
5,
x + TILE_SIZE / 2,
y,
TILE_SIZE
);
gradient.addColorStop(0, color);
gradient.addColorStop(1, "transparent");
ctx.fillStyle = gradient;
ctx.fillRect(x - TILE_SIZE / 2, y - TILE_SIZE, TILE_SIZE * 2, TILE_SIZE * 3);
// Core
ctx.fillStyle = color;
ctx.fillRect(x, y - TILE_SIZE / 2, TILE_SIZE, TILE_SIZE * 2);
ctx.restore();
}
function drawEnd(tile, x, y) {
ctx.save();
// Glow
const gradient = ctx.createRadialGradient(
x + TILE_SIZE / 2,
y + TILE_SIZE / 2,
5,
x + TILE_SIZE / 2,
y + TILE_SIZE / 2,
TILE_SIZE
);
gradient.addColorStop(0, "orange");
gradient.addColorStop(1, "transparent");
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(x + TILE_SIZE / 2, y + TILE_SIZE / 2, TILE_SIZE, 0, 2 * Math.PI);
ctx.fill();
// Core circle
ctx.fillStyle = "orange";
ctx.beginPath();
ctx.arc(x + TILE_SIZE / 2, y + TILE_SIZE / 2, TILE_SIZE / 3, 0, 2 * Math.PI);
ctx.fill();
// Ring
ctx.strokeStyle = "white";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(x + TILE_SIZE / 2, y + TILE_SIZE / 2, TILE_SIZE / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.restore();
}
function update() {
if (paused) return;
applyPhysics();
handleCollisions();
checkBlockCrush();
updateCamera();
checkLevelComplete();
}
/* Reset, Loop, Check for Complete, Pause, Init */
function resetPlayer() {
player.gamemode = "cube";
levelCompleted = false;
findStart();
music.currentTime = 0;
cameraX = 0;
player.velY = 0;
player.gravityState = 1;
jumpBuffered = false;
activatedPads.clear();
activatedOrbs.clear();
activatedPortals.clear();
console.log("Player reset");
}
function gameLoop() {
update();
render();
requestAnimationFrame(gameLoop);
}
function checkLevelComplete() {
const levelWidth = levelData.level1[0].length * TILE_SIZE;
if (player.x > levelWidth - TILE_SIZE / 2) {
completeLevel();
}
}
function completeLevel() {
if (levelCompleted) return;
levelCompleted = true;
paused = true;
music.pause();
console.log("Level Complete!");
}
function togglePause() {
paused = !paused;
if (paused) {
music.pause();
console.log("Game paused");
} else {
music.play().catch(() => { });
console.log("Game unpaused");
}
}
function init() {
setupInput();
findStart();
gameLoop();
music.play().catch(() => { });
console.log("Game initialized");
}
init();
</script>
</body>
</html>