factor to nodejs application

This commit is contained in:
biglyderv 2024-11-25 14:14:20 -05:00
parent 8cb80b9cbe
commit b237b857c7
10 changed files with 764 additions and 0 deletions

67
static/assets/head.svg Normal file
View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="61.553852"
height="183.03311"
viewBox="0 0 61.553852 183.03311"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
sodipodi:docname="head.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#000000"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="2.9077441"
inkscape:cx="20.806508"
inkscape:cy="88.384669"
inkscape:window-width="1860"
inkscape:window-height="1004"
inkscape:window-x="30"
inkscape:window-y="46"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<marker
style="overflow:visible"
id="Triangle"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Triangle arrow"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135" />
</marker>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-19.223075,68.033118)">
<path
style="fill:#000000;fill-opacity:1;stroke:#fcf9f9;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Triangle);paint-order:stroke fill markers"
d="M 50,110 V -32.52237"
id="path1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

72
static/assets/player.svg Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
sodipodi:docname="player.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#000000"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="8.2243423"
inkscape:cx="66.023517"
inkscape:cy="55.080392"
inkscape:window-width="1860"
inkscape:window-height="1004"
inkscape:window-x="30"
inkscape:window-y="46"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(10,10)">
<circle
style="fill:#000000;fill-opacity:1;stroke:#fcf9f9;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path1"
cx="50"
cy="50"
r="50" />
<path
id="circle1"
style="fill:#ffffff;stroke:#fcf9f9;stroke-width:3.98197;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers"
d="m 42.827076,40.730564 c -0.530179,8.374653 -2.920163,15.194007 -6.522366,15.194007 -3.602203,0 -6.640889,-6.803426 -6.522366,-15.194007 0.11532,-8.163806 13.616818,-9.036609 13.044732,0 z"
sodipodi:nodetypes="ssss" />
<path
id="ellipse1"
style="fill:#ffffff;stroke:#fcf9f9;stroke-width:3.98197;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers"
d="m 70.217656,40.546146 c 0,8.391418 -2.920163,15.194007 -6.522366,15.194007 -3.602203,0 -6.522366,-6.802589 -6.522366,-15.194007 0,-8.391418 3.092117,-16.931967 6.694321,-16.931967 3.602203,0 6.350411,8.540549 6.350411,16.931967 z"
sodipodi:nodetypes="sssss" />
<path
style="fill:none;fill-opacity:1;stroke:#fcf9f9;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 25.854918,30.643384 H 47.629179"
id="path2" />
<path
style="fill:none;fill-opacity:1;stroke:#fcf9f9;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 53.246967,18.854004 c 1.048608,-8.377641 -1.071223,-7.088082 11.233085,-7.805733 11.451296,-0.667899 9.895156,3.566952 10.541176,7.805733"
id="path3"
sodipodi:nodetypes="csc" />
<path
style="fill:none;fill-opacity:1;stroke:#fcf9f9;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 31.743312,69.350998 H 64.911025"
id="path4" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

64
static/index.css Normal file
View file

@ -0,0 +1,64 @@
:root {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
color: rgb(44, 41, 53);
}
section {
max-width: 90vw;
width: 800px;
margin-left: auto;
margin-right: auto;
background: rgb(255, 255, 255);
border: solid rgb(200, 200, 200) 2px;
border-radius: 10px;
min-height: 500px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
text-align: center;
margin: 10px;
margin-bottom: 0;
height: 40px;
line-height: 40px;
}
p {
margin: 10px;
}
.message span {
display: block;
text-align: center;
color: rgb(255, 255, 255);
font-weight: bold;
font-size: 50px;
}
.ui-text {
white-space: pre-wrap;
font-weight: bold;
padding: 10px;
}
#canvas {
width: 768px;
height: 768px;
/*image-rendering: pixelated;*/
background: rgb(63, 63, 71);
}
section#main {
width: 1024px;
}
@media (max-width: 1920px) {
#canvas {
width: min(512px, 90vw);
height: min(512px, 90vw);
}
}

27
static/index.html Normal file
View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='index.css'>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<section id='main'>
<h1>UniButton</h1>
<p>The <b>left mouse button</b> is the only input. No keyboard, moving the mouse, or anything else.</p>
<p>Click to launch... figure out the rest of the combos yourself.</p>
<p><i>Created by <a href='https://zenoverse.net/'>Onez</a>. Join our <a
href='https://discord.gg/EpsRZrHswBu'>Discord</a>!</i></p>
<div>
<canvas id='canvas'></canvas>
</div>
<div class='ui-text'>
placeholder
</div>
</section>
<script src='js/player.js'></script>
<script src='js/index.js'></script>
</body>
</html>

99
static/js/index.js Normal file
View file

@ -0,0 +1,99 @@
const cs = 1024;
const assets = [
'assets/player.svg',
'assets/head.svg',
];
function Game() {
let assetsIn = {};
for (let asset in assets) {
assetsIn[asset] = new Image();
assetsIn[asset].src = assets[asset];
}
let canvas = document.querySelector("#canvas");
let player = new Player(true);
let entities = [player];
for (let i = 0; i < 50; i++) {
entities.push(new Player())
}
canvas.width = canvas.height = cs;
this.canvas = canvas;
this.ctx = canvas.getContext("2d");
this.assetsIn = assetsIn;
this.player = player;
this.entities = entities;
}
Game.prototype.main = function () {
let { entities, player } = this;
if (player.health <= 0) {
return;
}
for (let ent of entities) {
ent.handleTick(this);
}
}
// todo: move into its own file
Game.prototype.render = function () {
let { ctx, assetsIn, entities, player } = this;
ctx.clearRect(0, 0, cs, cs);
ctx.save();
ctx.translate(player.camera.x + cs / 2, player.camera.y + cs / 2);
for (let ent of entities) {
ctx.save();
ctx.translate(ent.pos.x, ent.pos.y);
ctx.rotate(ent.rot);
ctx.drawImage(assetsIn[1], -64 / 2, -128, 64, 128);
ctx.restore();
ctx.drawImage(assetsIn[0], ent.pos.x - 64 / 2, ent.pos.y - 64 / 2, 64, 64);
}
ctx.restore();
if (player.health <= 0) {
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.fillRect(0,0,cs,cs);
ctx.fillStyle = 'rgb(255,255,255)';
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "bold 48px sans-serif";
ctx.fillText('You died! Click to respawn',cs/2,cs/2);
}
}
Game.prototype.ui = function() {
document.querySelector('.ui-text').textContent = `HP: ${this.player.health}`
}
Game.prototype.click = function () {
let { player } = this;
if (player.health <= 0) {
game = new Game();
} else {
player.bump();
}
}
var game = new Game();
setInterval(function () { game.main() }, 1000 / 60);
setInterval(function () { game.render() }, 1000 / 60);
setInterval(function () { game.ui() }, 1000 / 10);
game.canvas.onclick = () => game.click();

71
static/js/player.js Normal file
View file

@ -0,0 +1,71 @@
function Player(you) {
let pos = { x: Math.random() * 5000 - 50, y: Math.random() * 5000 - 50 };
let camera = { x: -pos.x, y: -pos.y };
let vel = { x: 0, y: 0 };
this.camera = camera;
this.pos = pos;
this.vel = vel;
this.rot = 0;
this.dir = 1;
this.ticks = 0;
this.health = 100;
this.you = you;
}
Player.prototype.bump = function () {
let player = this;
if (player.ticks < 10) {
player.dir *= -1;
}
player.vel.x *= 0.3;
player.vel.y *= 0.3;
player.vel.x += Math.sin(player.rot) * 12;
player.vel.y -= Math.cos(player.rot) * 12;
player.ticks = 0;
}
Player.prototype.handleTick = function(game) {
let { player } = game;
let ent = this;
ent.pos.x += ent.vel.x;
ent.pos.y += ent.vel.y;
ent.vel.x *= 0.9;
ent.vel.y *= 0.9;
ent.rot += 0.03 * ent.dir;
ent.rot = ent.rot % (Math.PI * 10);
ent.camera.x = -ent.pos.x * 0.1 + ent.camera.x * 0.9;
ent.camera.y = -ent.pos.y * 0.1 + ent.camera.y * 0.9;
ent.ticks++;
let dist = ((ent.pos.x - player.pos.x) ** 2) + ((ent.pos.y - player.pos.y) ** 2);
let dp = (Math.sin(ent.rot) * (ent.pos.x - player.pos.x))
- (Math.cos(ent.rot) * (ent.pos.y - player.pos.y));
dp /= dist;
dp *= 10;
if (ent.you) return;
if (Math.random() < -dp && Math.random() > 3 / (ent.ticks+2)) {
ent.bump();
}
if (Math.sqrt(dist) < 96 / 2) {
player.health --;
}
}