1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<!doctype html>
<meta charset='utf-8'>
<title>🤠Joe Hat</title>
<meta name="viewport" content="width=520, user-scalable=no">
<div id='instructions'>Press any button to jump. Get to the top, avoid robots</div>
<span id='score_box'>Level: 0, Heath: 100, Score: 0</span><br>
<canvas id='canvas' style="border:5px solid;border-radius:10px;" width=500 height=600></canvas>
<style>*, html{-webkit-user-select:none;touch-action:none;user-select:none;</style>
<script>
var drawing = canvas.getContext("2d");
Object.assign(drawing, {textAlign: "center", textBaseline: "middle", font: "14px serif"});
var player = { health: 100, treasure:0, colour: "darkblue",
x: 40, y: canvas.height-30, width: 20, height: 40,
speed_x: -105, speed_y: 0};
var entry = {colour:"#000000", width: 20, height: 35, x: 480, y: 565};
var exit = {colour:"#880000", width: 20, height: 35, x: 10, y: 30};
var level_seeds = [67,52,50,54,40,21,60,28,3,5,8,9,11,49,16,12,18,29,19,10];
var game = {level: 0, last_frame: 0, items: [], gravity: 200, seed_number: 0};
function non_random_number() {
game.seed_number = (((+game.seed_number) + 123456) * 654321).toString().slice(-10, -1);
return parseFloat("0."+game.seed_number);
}
var between = (min, max) => min+Math.floor(non_random_number() * ((max-min)+1));
var a_colour=()=>"#"+["334455","553344","445533","443355","335544","554433"][between(0,5)];
var enemy = {symbol:"🤖", colour:"#EE0000", width:10, height:18, speed_x:-50, damage:20};
var treasure = {colour: "#FFFF00", wealth: 1, width: 10, height: 10};
function create_level(){
game.seed_number = level_seeds[game.level++] || game.seed_number;
game.items = [entry, exit, player = Object.assign(player, {x: 470, y: canvas.height-30})];
for (var y = canvas.height-95; y > 40; y-= 95)
for (var x = 0, width=0; x < canvas.width; x+= width + between(45, 90)){
if (x == 0 && between(0,2) == 2) x += between(45, 90);
width = Math.min(between(50, 250), canvas.width - x);
var platform = {x,y, width, colour: a_colour(), height: 10};
game.items.push(platform, {...treasure, x: between(x, x + width-10), y: y-20});
if (width > 50 && between(0,1))
game.items.push({...enemy, x:platform.x, y: y-25, platform});
}
canvas.style.background = (a_colour())+"44";
}
var draw_item = (item) => {
drawing.beginPath();
Object.assign(drawing,{strokeStyle:item.colour, lineWidth:4, fillStyle:item.colour+"CC"});
drawing.roundRect(item.x+1, item.y+1, item.width-2, item.height-2,[3]);
drawing.stroke();
drawing.fill();
if (item.symbol) drawing.fillText(item.symbol, item.x + item.width/2, item.y+item.width/2);
}
var feet_apart = 10, feet_move = 60;
var draw_player=(interval)=>{
drawing.beginPath();
[ [10, 15], [10, 28],
[10 - (player.grounded ? feet_apart : 10), 40], [10, 28],
[10 + (player.grounded ? feet_apart : 10), 40],
[0, 20, true], [20, 20],
].forEach((point, index)=>{
if (index == 0 || point[2]) drawing.moveTo(player.x + point[0], player.y+point[1]);
else drawing.lineTo(player.x + point[0], player.y+point[1]);
});
Object.assign(drawing, {lineWidth: 4, strokeStyle: player.colour, fillStyle: "black"});
drawing.stroke();
feet_apart -= feet_move * interval;
feet_move = (feet_apart <= 0) ? -60 : (feet_apart >=10) ? 60 : feet_move;
drawing.fillText(player.health > 0 ? "🤠": "💀", player.x+10, player.y+10);
};
var collides=(a,b)=> a.right > b.x && b.right > a.x && a.bottom > b.y && b.bottom > a.y;
var collect_treasure=(t)=> t.deleted = player.treasure = (t.wealth + (player.treasure|0));
var last_time = performance.now();
requestAnimationFrame(function do_one_frame(now){
var interval = Math.min(0.05, (now - last_time)/1000);
if (interval == 0) return requestAnimationFrame(do_one_frame);
last_time = now;
player.last_health = player.health;
if (game.items.length == 0 || collides(player,exit)) create_level();
drawing.clearRect(0,0, canvas.width, canvas.height);
game.items.forEach(i=>{
i.right = (i.x = i.x + (i.speed_x|0) * interval) + i.width;
i.bottom = (i.y = i.y + (i.speed_y|0) * interval) + i.height;
if (i.grounded = i.bottom > canvas.height) i.y = canvas.height - (i.height-1);
if ((i.right>canvas.width && i.speed_x>0) || (i.x<0 && i.speed_x<0)) i.speed_x *=-1;
if (i.platform && i.right > i.platform.right && i.speed_x > 0) i.speed_x *=-1;
if (i.platform && i.x < i.platform.x && i.speed_x < 0) i.speed_x *=-1;
if (i !== player && !i.deleted) draw_item(i);
if (i== player || i.deleted || i == entry || !collides(i, player)) return;
player.health -= (i.damage|0) * interval;
if (i.wealth) return collect_treasure(i);
if (player.bottom < i.y + 5) player.grounded = player.y = i.y - (player.height-1);
else if (player.y > i.bottom - 5 && player.speed_y < 0) player.speed_y *= -1;
else if (player.right < i.x + 5 && player.speed_x > 0) player.speed_x *= -1;
else if (player.x > i.x - 5 && player.speed_x < 0) player.speed_x *= -1;
});
draw_player(interval);
player.speed_y = player.grounded? 0 : Math.min(player.speed_y+game.gravity*interval, 300);
canvas.style.borderColor = (player.health < player.last_health) ? "red" : "black";
score_box.textContent = `Level: ${game.level}, Score: ${player.treasure},
Health: ${(Math.max(0, Math.ceil(player.health*10)/10)).toFixed(1)}%`;
if (player.health > 0) requestAnimationFrame(do_one_frame);
});
onkeydown = onmousedown = ontouchstart = (e)=>{if (player.grounded) player.speed_y = -200};
</script>