Compare commits

...

2 Commits

Author SHA1 Message Date
Neale Pickett b135069851 Clean up animation code, begin work on login 2023-09-11 17:29:14 -06:00
Neale Pickett 18c5f044cc stub submit event 2023-09-08 18:11:36 -06:00
6 changed files with 154 additions and 111 deletions

View File

@ -5,118 +5,150 @@ function randint(max) {
const MILLISECOND = 1 const MILLISECOND = 1
const SECOND = MILLISECOND * 1000 const SECOND = MILLISECOND * 1000
class Line { class Point {
constructor(x, y) {
this.x = x
this.y = y
}
/** /**
* @param {CanvasRenderingContext2D} ctx canvas context * Add n to this.
* @param {Number} hue Hue, in % of one circle [0,tau) *
* @param {Number} a First point of line * @param {Point} n What to add to this
* @param {Number} b Second point of line * @returns {Point}
*/ */
constructor(ctx, hue, a, b) { Add(n) {
this.ctx = ctx return new Point(this.x + n.x, this.y + n.y)
}
/**
* Subtract n from this.
*
* @param {Point} n
* @returns {Point}
*/
Subtract(n) {
return new Point(this.x - n.x, this.y - n.y)
}
/**
* Add velocity, then bounce point off box defined by points at min and max
* @param {Point} velocity
* @param {Point} min
* @param {Point} max
* @returns {Point}
*/
Bounce(velocity, min, max) {
let p = this.Add(velocity)
if (p.x < min.x) {
p.x += (min.x - p.x) * 2
velocity.x *= -1
}
if (p.x > max.x) {
p.x += (max.x - p.x) * 2
velocity.x *= -1
}
if (p.y < min.y) {
p.y += (min.y - p.y) * 2
velocity.y *= -1
}
if (p.y > max.y) {
p.y += (max.y - p.y) * 2
velocity.y *= -1
}
return p
}
/**
*
* @param {Point} p
* @returns {Boolean}
*/
Equal(p) {
return (this.x == p.x) && (this.y == p.y)
}
}
class QixLine {
/**
* @param {Number} hue
* @param {Point} a
* @param {Point} b
*/
constructor(hue, a, b) {
this.hue = hue this.hue = hue
this.a = a this.a = a
this.b = b this.b = b
} }
bounce(point, v) {
let ret = [
point[0] + v[0],
point[1] + v[1],
]
if ((ret[0] > this.ctx.canvas.width) || (ret[0] < 0)) {
v[0] *= -1
ret[0] += v[0] * 2
}
if ((ret[1] > this.ctx.canvas.height) || (ret[1] < 0)) {
v[1] *= -1
ret[1] += v[1] * 2
}
return ret
}
Add(hue, a, b) {
return new Line(
this.ctx,
(this.hue + hue) % 1.0,
this.bounce(this.a, a),
this.bounce(this.b, b),
)
}
Draw() {
this.ctx.save()
this.ctx.strokeStyle = `hwb(${this.hue}turn 0% 50%)`
this.ctx.beginPath()
this.ctx.moveTo(this.a[0], this.a[1])
this.ctx.lineTo(this.b[0], this.b[1])
this.ctx.stroke()
this.ctx.restore()
}
} }
class LengoBackground { /**
constructor() { * Draw a line dancing around the screen,
this.canvas = document.createElement("canvas") * like the video game "qix"
document.body.insertBefore(this.canvas, document.body.firstChild) */
this.canvas.style.position = "fixed" class QixBackground {
this.canvas.style.zIndex = -1000 constructor(ctx) {
this.canvas.style.opacity = 0.3 this.ctx = ctx
this.canvas.style.top = 0 this.min = new Point(0, 0)
this.canvas.style.left = 0 this.max = new Point(this.ctx.canvas.width, this.ctx.canvas.height)
this.canvas.style.width = "99vw" this.box = this.max.Subtract(this.min)
this.canvas.style.height = "99vh"
this.canvas.width = 2000
this.canvas.height = 2000
this.ctx = this.canvas.getContext("2d")
this.ctx.lineWidth = 1
this.lines = [] this.lines = [
for (let i = 0; i < 18; i++) { new QixLine(
this.lines.push( 0,
new Line(this.ctx, 0, [0, 0], [0, 0]) new Point(randint(this.box.x), randint(this.box.y)),
new Point(randint(this.box.x), randint(this.box.y)),
) )
]
while (this.lines.length < 18) {
this.lines.push(this.lines[0])
} }
this.velocities = { this.velocity = new QixLine(
hue: 0.001, 0.001,
a: [20 + randint(10), 20 + randint(10)], new Point(1 + randint(this.box.x / 200), 1 + randint(this.box.y / 200)),
b: [5 + randint(10), 5 + randint(10)], new Point(1 + randint(this.box.x / 200), 1 + randint(this.box.y / 200)),
} )
this.nextFrame = performance.now()-1
this.frameInterval = 100 * MILLISECOND
//addEventListener("resize", e => this.resizeEvent()) setInterval(() => this.animate(), SECOND/6)
//this.resizeEvent()
//this.animate(this.nextFrame)
setInterval(() => this.animate(this.nextFrame+1), SECOND/6)
} }
/** /**
* Animate one frame * Animate one frame
*
* @param {DOMHighResTimeStamp} timestamp
*/ */
animate(timestamp) { animate() {
if (timestamp >= this.nextFrame) { this.lines.shift()
this.lines.shift() let lastLine = this.lines[this.lines.length - 1]
let lastLine = this.lines.pop() let nextLine = new QixLine(
let nextLine = lastLine.Add(this.velocities.hue, this.velocities.a, this.velocities.b) (lastLine.hue + this.velocity.hue) % 1.0,
this.lines.push(lastLine) lastLine.a.Bounce(this.velocity.a, this.min, this.max),
this.lines.push(nextLine) lastLine.b.Bounce(this.velocity.b, this.min, this.max),
)
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height) this.lines.push(nextLine)
for (let line of this.lines) {
line.Draw() this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
} for (let line of this.lines) {
this.nextFrame += this.frameInterval this.ctx.save()
this.ctx.strokeStyle = `hwb(${line.hue}turn 0% 50%)`
this.ctx.beginPath()
this.ctx.moveTo(line.a.x, line.a.y)
this.ctx.lineTo(line.b.x, line.b.y)
this.ctx.stroke()
this.ctx.restore()
} }
//requestAnimationFrame((ts) => this.animate(ts))
} }
} }
function init() { function init() {
new LengoBackground() let canvas = document.createElement("canvas")
canvas.width = 640
canvas.height = 640
canvas.classList.add("wallpaper")
document.body.insertBefore(canvas, document.body.firstChild)
let ctx = canvas.getContext("2d")
new QixBackground(ctx)
} }
if (document.readyState === "loading") { if (document.readyState === "loading") {

View File

@ -22,6 +22,17 @@ h1 {
a:any-link { a:any-link {
color: #b9cbd8; color: #b9cbd8;
} }
canvas.wallpaper {
position: fixed;
display: block;
z-index: -1000;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
opacity: 0.3;
image-rendering: pixelated;
}
.notification { .notification {
background: #ac8f3944; background: #ac8f3944;
} }

View File

@ -4,15 +4,15 @@
<title>MOTH</title> <title>MOTH</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link rel="icon" href="luna-moth.svg">
<link rel="stylesheet" href="basic.css"> <link rel="stylesheet" href="basic.css">
<script src="moth.js"></script> <script src="moth.mjs" type="module"></script>
<link rel="manifest" href="manifest.json"> <script src="background.mjs" type="module"></script>
</head> </head>
<body> <body>
<h1 id="title">MOTH</h1> <h1 id="title">MOTH</h1>
<section> <main>
<div id="messages"> <div id="messages notification">
<div id="notices"></div>
</div> </div>
<form id="login"> <form id="login">
@ -22,8 +22,7 @@
</form> </form>
<div id="puzzles"></div> <div id="puzzles"></div>
</main>
</section>
<nav> <nav>
<ul> <ul>
<li><a href="scoreboard.html">Scoreboard</a></li> <li><a href="scoreboard.html">Scoreboard</a></li>

View File

@ -1,9 +0,0 @@
{
"name": "Monarch of the Hill",
"short_name": "MOTH",
"start_url": ".",
"display": "standalone",
"background_color": "#282a33",
"theme_color": "#ECB",
"description": "The MOTH CTF engine"
}

View File

@ -2,9 +2,10 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>Puzzle</title> <title>Puzzle</title>
<link rel="stylesheet" href="basic.css">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="icon" href="luna-moth.svg">
<link rel="stylesheet" href="basic.css">
<script src="puzzle.mjs" type="module"></script> <script src="puzzle.mjs" type="module"></script>
<script src="background.mjs" type="module"></script> <script src="background.mjs" type="module"></script>
</head> </head>
@ -20,11 +21,9 @@
<ul id="files"></ul> <ul id="files"></ul>
<p>Puzzle by <span id="authors">[loading]</span></p> <p>Puzzle by <span id="authors">[loading]</span></p>
</section> </section>
<form> <form class="answer">
<input type="hidden" name="cat">
<input type="hidden" name="points">
Team ID: <input type="text" name="id"> <br> Team ID: <input type="text" name="id"> <br>
Answer: <input type="text" name="answer" id="answer"> <span id="answer_ok"></span><br> Answer: <input type="text" name="answer"> <span id="answer_ok"></span><br>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
</main> </main>

View File

@ -1,5 +1,13 @@
import * as moth from "./moth.mjs" import * as moth from "./moth.mjs"
/** Stores the current puzzle, globally */
let puzzle = null
function submit(event) {
event.preventDefault()
console.log(event)
}
function puzzleElement(clear=true) { function puzzleElement(clear=true) {
let e = document.querySelector("#puzzle") let e = document.querySelector("#puzzle")
if (clear) { if (clear) {
@ -16,7 +24,7 @@ function error(message) {
async function loadPuzzle(category, points) { async function loadPuzzle(category, points) {
let server = new moth.Server() let server = new moth.Server()
let puzzle = server.GetPuzzle(category, points) puzzle = server.GetPuzzle(category, points)
await puzzle.Populate() await puzzle.Populate()
let title = `${category} ${points}` let title = `${category} ${points}`
@ -49,6 +57,9 @@ function hashchange() {
} }
function init() { function init() {
for (let e of document.querySelectorAll("form.answer")) {
e.addEventListener("submit", submit)
}
window.addEventListener("hashchange", hashchange) window.addEventListener("hashchange", hashchange)
hashchange() hashchange()
} }