From a2866a71d75167b09e0df13525da035508299519 Mon Sep 17 00:00:00 2001 From: ilitirit Date: Wed, 10 May 2023 23:40:57 +0200 Subject: [PATCH] Add time-bombs --- Expedience.Web/Expedience.Web.csproj | 5 + Expedience.Web/wwwroot/images/vuln-stack.png | Bin 0 -> 3128 bytes Expedience.Web/wwwroot/js/time-bomb.js | 389 +++++++++++++++++++ Expedience.Web/wwwroot/time-bomb.html | 53 +++ 4 files changed, 447 insertions(+) create mode 100644 Expedience.Web/wwwroot/images/vuln-stack.png create mode 100644 Expedience.Web/wwwroot/js/time-bomb.js create mode 100644 Expedience.Web/wwwroot/time-bomb.html diff --git a/Expedience.Web/Expedience.Web.csproj b/Expedience.Web/Expedience.Web.csproj index 5abeb4c..ae59c19 100644 --- a/Expedience.Web/Expedience.Web.csproj +++ b/Expedience.Web/Expedience.Web.csproj @@ -8,6 +8,11 @@ Linux + + + + + diff --git a/Expedience.Web/wwwroot/images/vuln-stack.png b/Expedience.Web/wwwroot/images/vuln-stack.png new file mode 100644 index 0000000000000000000000000000000000000000..b3c35bb054896f56c07ec2ef7761ddfcd41a0be3 GIT binary patch literal 3128 zcmZ{mXHXLk(#Dg3A)u%U#R$@?l#ozEl|ZP5BAtjdsZv5ydRHLuCqO_EsUi^+FfcB;tJ(1d_b zvmO7g?y2zK);BW(0K!B8fSCILz~N~r<_7?9_jGX85dctm3;_tU)RF| z{{Ej8HJ4@r0B0o)uow%EnV(On!9u;zo@;O4IzBeUrA#?%eCNEL?g|guc5swA7jQ0K zQCE3cp$|px~^|;2jHBDi@n3kX^9W7kF&O=<+d);T({=Fm&$LABT+J z&CDOz288Xk9kW(d^3SKd@90t_)V?`nk1Z|PyDDteqX?xVhktHv28M<8`Ju5`vs~TQ z>@?i3S^jD;|A`_aG1qbqu(%Z>RFKUr5pY#KG2NG*fB2no`>&8Di9|xHsa3=9q+kn z1srx`qg@$Vsq>*+2*6)6*6?C6L+#d->}64TkV`Znt-Mz5qUq-7 z4K)l%laaAKlr@E)U44MqWYvnfIBr2 zqaAhLys{)m#zZIgqXK9IQs686)Gv}&?l$8kvS)$&Y8O%9rS2&?eDn6Z=GMfC%N`SF zb3$HlV1h_Rj}iJxh>OIz!PVgIGP1XQ1>;JMy=Cz59SGp?_~Z%LEK<>x^88Z8yZ2ib z{zGL8kcnNP3`COt&l(p>pm6aH*O*iE1JAQzXOhk;jK&;jd=|>Z*^Yq5^R0d5$6j$b zIXaGIKksS;1+vDl5hEgo2NN6xbs4GnNoUc!#MaEh-qE&5WVYT%E?|EZ+A=TRtMm%o zRzPr&f+?vsUbZM$X=|(Zl5lz`b03n|=OPO%>RikS+_4uo&w=UNsiLbo$_OB+z*vT^ zppQ=5{+T?&EikC$iQQpuM1jN_6W6fbEn2Gi%oJMmi#8GBtl3agQzNwV?(l-u8s6n0 z+v5S+yePQcsCiU=PAO)9$@IZTBJJ~8>IEZyCm^-`(ehe;CDc!Pk;TIf7~ftLR)Eug z(&=3a+*F@AQb~Vd_ILPw=u$uBEW$#QY`)ojeKp5uIduffZtb@bOFsM1PG{1CU#u85 zRm?wJA;=|!xiyogiuf!AXZDGN+)K5B^?d3!K{;cR-uMrsy|g;wdrHFiSkc$DBa^Hr z(w~7g5s?=gnX{9+klYOVBA@Za-QPqT8y+Og#cCD*xc+br?6mVYHaJ|3sbh|ZF>^jvZ+~{f-D)o%9UY~5L`=iD zM})T2HsG20{7D{TTuI>eCphW|vL-ILKJktr9|saFm%pF`w}~$l|HNtM;1C!XsD1Os z@EsmImDYT4Q|UYn%WjFW5UAIpI86e&4YN*Gh2yLfA?Lu6U4HukowxTaU6nr_EhZjlXttmDOYZsxC- z@Zcys%#0_mad4FW?VFde6G`@-VF$tl_r;L^cQ7h$m!ffSwJf(-B4h<@);8iE4U~tW zoX+nJ4^E*S3?jKR>R=Et1nbw$#0(3L=%32L$w}>S?bcb=(8oURhWK=Ge1nr0%DQPM zj|L4epZy8xk}N2jN~J|CENa2h4;_7t)~mvteXhPt4%0Dhvi>v8Gx|A^7Mi?p12zf! zk#MeaiQ=ATA+bmN)z;x5e0jN)xr8D4pl>>8Vj)y`{cfRwvyYtEEex%%mr~xw#5}^N zmnh0$kBM+`OH%+AkE)rjT6v^GjVt|7vR-#9Gw3es{+`^^6-(=b-P07S0rKu}ID0$X zbC)RCZ8LAfnC7=}+}&BI5C?@PSsdi{am6aJr47m7&Fp##?P5VvABeRiGiw;UUbe{8 zzv*}5lj%D+{+E+F@BFD<$myFFGs0} zam6ryo`1*?WH0jK4S#y=d8X>3GhPcBP@Txsrz{`vrJz#t`ER?m)$DtyNlRCmfU1d$ zgo3CF!lX#!u&S(%vizHoMGG&vFAYZd1`NS3#zu))R{Lkkxxa@wda1FqcD!Qgny^TX zWMjEN(!|9~QWNh*i}@*4#9^`xTTN3j%C^EXx^n#5YKknJ?Ix}@WZ zsE*Yn0i2)3vLmi{yNBN0X;-V3=5nw%#Z9c>>)k!m4;OIOdWj_DZA>)XzcU8&`t*<& z?%6y4VB2j$IfuR);(;3zg?B!~3dd4Wmhr70cE#d zZoiDHa1=#fR~Ii7@l}{{sp`# zk;)}Yv&4{##l=}dM1E=9{8Ubn>h#~DC6c3n&Gr}pejN-3BLE~l=5>~OfO?n6)Y91C zd^v%RAd9ob!svi?ME3QakH_jdHyp7)a&Db(fzn@U^3@3zRhvVBX9PvGVpljKLd3W5 z+cu$t@MY_<={6f`rqHg4)`XgavoUS8~BkcORWj+?%f@;X5)EM&93pzR&B?*xw%=t zQ-ImE*$rcld4y**f8?Zo1wP-YO*E_Wx>r4tfVH;WKI!4BKVG`@=%Yx?X4|_TK`o`? zQvvn6P;E)N5!YBr#%eSZ>C(8E(@}W%VHlcS2zIYT17gGvzKUt=Sd!PUbXCjw?m!Wj zoDs=w<)8;bDxl+QL5x*V)o|(6v~BdnENyV2J9n>&Sbk}*cL^Q2ncilk!Xql>mm>CQ z$(-5yYtl-a1LxLlXv{OhfihCGi6KS*RsN#Hjuhij)b(TNP;E>!ORS=xmS2633sx}; zwMWb3WBn6&@q@wlgGYzqCTxYF9XA)YXrPmNh?=f&>j1{L;cqdcVuq(=;W-tVzd(Zo zg1qQ)BmMM|775a^3371?a#gt<;Cdi_@% literal 0 HcmV?d00001 diff --git a/Expedience.Web/wwwroot/js/time-bomb.js b/Expedience.Web/wwwroot/js/time-bomb.js new file mode 100644 index 0000000..83b5a66 --- /dev/null +++ b/Expedience.Web/wwwroot/js/time-bomb.js @@ -0,0 +1,389 @@ +const canvas = document.getElementById('gameCanvas'); +const ctx = canvas.getContext('2d'); + +const arenaSize = 500; +const subSquareSize = arenaSize / 3; +const clockRadius = subSquareSize / 9; +const playerSize = subSquareSize / 10; +const handLength = clockRadius; +const triangleOpacity = 0.5; + +let rotationStep = 0; +const maxRotationSteps = 6; +let rotationStarted = false; +let rotationComplete = false; +let randomLayout = Math.floor(Math.random() * 5); +let lastRotationTime = performance.now(); +let gameStopped = false; + +let player = null; +let clocks = null; + +let deathImage = new Image(); + +// Set the source of the image +deathImage.src = "../images/vuln-stack.png"; + + +function rotateClocks(elapsedTime) { + if (rotationStep < maxRotationSteps && elapsedTime >= 1000) { + clocks.forEach(clock => clock.rotateHand()); + rotationStep++; + lastRotationTime = performance.now(); + } else if (rotationStep === maxRotationSteps) { + rotationComplete = true; + } +} + +function resetRotation() { + rotationStep = 0; + rotationComplete = false; +} + +class Clock { + constructor(x, y, handDirection) { + this.x = x; + this.y = y; + this.handDirection = handDirection; + } + + drawArrow() { + ctx.lineWidth = 1; + ctx.strokeStyle = "black"; + // Draw the line + ctx.beginPath(); + ctx.moveTo(this.x, this.y); + ctx.lineTo( + this.x + handLength * Math.cos(this.handDirection * Math.PI / 180), + this.y + handLength * Math.sin(this.handDirection * Math.PI / 180) + ); + ctx.stroke(); + + // Draw the arrowhead + var arrowSize = 7; // Change the value to adjust the size of the arrowhead + var arrowAngle = Math.PI / 9; // Change the value to adjust the angle of the arrowhead + var angle = Math.atan2(handLength * Math.sin(this.handDirection * Math.PI / 180), handLength * Math.cos(this.handDirection * Math.PI / 180)); + ctx.beginPath(); + ctx.moveTo( + this.x + handLength * Math.cos(this.handDirection * Math.PI / 180), + this.y + handLength * Math.sin(this.handDirection * Math.PI / 180) + ); + ctx.lineTo( + this.x + (handLength - arrowSize) * Math.cos(angle - arrowAngle), + this.y + (handLength - arrowSize) * Math.sin(angle - arrowAngle) + ); + ctx.lineTo( + this.x + (handLength - arrowSize) * Math.cos(angle + arrowAngle), + this.y + (handLength - arrowSize) * Math.sin(angle + arrowAngle) + ); + ctx.closePath(); + ctx.fillStyle = "red"; // Set the fill color to red + ctx.fill(); + } + + draw() { + ctx.beginPath(); + ctx.arc(this.x, this.y, clockRadius, 0, 2 * Math.PI); + ctx.fillStyle = "gold"; + ctx.fill(); + ctx.strokeStyle = "orange"; + ctx.lineWidth = 3; + ctx.stroke(); + + this.drawArrow(); + ctx.stroke(); + } + + rotateHand() { + this.handDirection = (this.handDirection + 90) % 360; + } + +drawConal() { + const angle = 45; + const sideLength = 50; + + ctx.beginPath(); + ctx.moveTo(this.x, this.y); + ctx.lineTo( + this.x + sideLength * handLength * Math.cos((this.handDirection - angle) * Math.PI / 180), + this.y + sideLength * handLength * Math.sin((this.handDirection - angle) * Math.PI / 180) + ); + ctx.lineTo( + this.x + sideLength * handLength * Math.cos((this.handDirection + angle) * Math.PI / 180), + this.y + sideLength * handLength * Math.sin((this.handDirection + angle) * Math.PI / 180) + ); + ctx.closePath(); + ctx.fillStyle = `rgba(255, 255, 0, ${triangleOpacity})`; + ctx.fill(); + + + // Draw red lines + ctx.beginPath(); + ctx.moveTo(this.x, this.y); + ctx.lineTo( + this.x + sideLength * handLength * Math.cos((this.handDirection - angle) * Math.PI / 180), + this.y + sideLength * handLength * Math.sin((this.handDirection - angle) * Math.PI / 180) + ); + ctx.strokeStyle = 'red'; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(this.x, this.y); + ctx.lineTo( + this.x + sideLength * handLength * Math.cos((this.handDirection + angle) * Math.PI / 180), + this.y + sideLength * handLength * Math.sin((this.handDirection + angle) * Math.PI / 180) + ); + ctx.strokeStyle = 'red'; + ctx.stroke(); + ctx.strokeStyle = 'black'; + } +} + +const keysPressed = { + ArrowUp: false, W: false, w: false, + ArrowLeft: false, A: false, a: false, + ArrowDown: false, S: false, s: false, + ArrowRight: false, D: false, d: false +}; + +class Player { + constructor(x, y) { + this.x = x; + this.y = y; + this.hasMoved = false; + } + + draw() { + ctx.fillStyle = 'blue'; + ctx.fillRect(this.x - playerSize / 2, this.y - playerSize / 2, playerSize, playerSize); + } + + move(dx, dy) { + const newX = this.x + dx; + const newY = this.y + dy; + + if (newX >= playerSize / 2 && newX <= arenaSize - playerSize / 2) { + this.x = newX; + this.hasMoved = true; + } + if (newY >= playerSize / 2 && newY <= arenaSize - playerSize / 2) { + this.y = newY; + this.hasMoved = true; + } + } +} + +function getClockPositions(layout) { + switch (layout) { + case 0: + return [ + new Clock(subSquareSize, subSquareSize, 180), + new Clock(arenaSize - subSquareSize, subSquareSize * 2, 0) + ]; + + case 1: + return [ + new Clock(subSquareSize, subSquareSize, 180), + new Clock(subSquareSize, subSquareSize * 2, 180), + new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 0) + ]; + + case 2: + return [ + new Clock(subSquareSize, subSquareSize, 270), + new Clock(subSquareSize, subSquareSize * 2, 90), + new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 180) + ]; + + case 3: + return [ + new Clock(subSquareSize + (subSquareSize / 2), subSquareSize, 270), + new Clock(subSquareSize, subSquareSize * 2, 90), + new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 180) + ]; + + case 4: + return [ + new Clock(subSquareSize + (subSquareSize / 2), subSquareSize, 0), + new Clock(subSquareSize, subSquareSize * 2, 180), + new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 90) + ] + } +} + + +function pointInTriangle(px, py, ax, ay, bx, by, cx, cy) { + const v0x = cx - ax; + const v0y = cy - ay; + const v1x = bx - ax; + const v1y = by - ay; + const v2x = px - ax; + const v2y = py - ay; + + const d00 = v0x * v0x + v0y * v0y; + const d01 = v0x * v1x + v0y * v1y; + const d11 = v1x * v1x + v1y * v1y; + const d20 = v2x * v0x + v2y * v0y; + const d21 = v2x * v1x + v2y * v1y; + + const denominator = d00 * d11 - d01 * d01; + const a = (d11 * d20 - d01 * d21) / denominator; + const b = (d00 * d21 - d01 * d20) / denominator; + + return a >= 0 && b >= 0 && a + b <= 1; +} + +function checkCollisions() { + const px = player.x; + const py = player.y; + let collision = false; + + for (const clock of clocks) { + const ax = clock.x; + const ay = clock.y; + const angle = 45; + const sideLength = 50; + + const bx = clock.x + sideLength * handLength * Math.cos((clock.handDirection - angle) * Math.PI / 180); + const by = clock.y + sideLength * handLength * Math.sin((clock.handDirection - angle) * Math.PI / 180); + const cx = clock.x + sideLength * handLength * Math.cos((clock.handDirection + angle) * Math.PI / 180); + const cy = clock.y + sideLength * handLength * Math.sin((clock.handDirection + angle) * Math.PI / 180); + + if (pointInTriangle(px, py, ax, ay, bx, by, cx, cy)) { + collision = true; + break; + } + } + + return collision; +} + +function displaySafeStatus(isSafe) { + if (isSafe) + succeeded(); + else + failed(); +} + + +function succeeded() { + drawText("SUCCESS!", 'green'); +} + +function failed() { + ctx.drawImage(deathImage, player.x - playerSize, player.y - playerSize, playerSize * 2, playerSize * 2); + drawText("FAILED!", 'red'); +} + +function drawText(text, colour) { + ctx.font = '35px Verdana'; // Set the font size and type + ctx.fillStyle = colour; // Set the fill color + ctx.textAlign = 'center'; // Set the horizontal alignment + ctx.textBaseline = 'middle'; // Set the vertical alignment + + const x = canvas.width / 2; // Set the x-coordinate of the text + const y = 80; // Set the y-coordinate of the text + + ctx.fillText(text, x, y); // Draw the text using fillText() +} + +function gameLoop(currentTime) { + + if (gameStopped) { + requestAnimationFrame(gameLoop); + return; + } + + ctx.fillStyle = "gray"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + if (rotationStarted === false && player.hasMoved) { + rotationStarted = true; + } + + const elapsedTime = currentTime - lastRotationTime; + + if (rotationStarted && !rotationComplete) { + rotateClocks(elapsedTime); + } + + const moveAmount = 4; + if (movingUp()) player.move(0, -moveAmount); + if (movingDown()) player.move(0, moveAmount); + if (movingLeft()) player.move(-moveAmount, 0); + if (movingRight()) player.move(moveAmount, 0); + + player.draw(); + clocks.forEach(clock => { + clock.draw(); + if (rotationComplete) { + clock.drawConal(); + } + }); + + if (rotationComplete) { + const isSafe = !checkCollisions(); + displaySafeStatus(isSafe); + stopGame(); + } + + requestAnimationFrame(gameLoop); +} + +function movingUp() { return keysPressed.ArrowUp || keysPressed.W || keysPressed.w; } +function movingDown() { return keysPressed.ArrowDown || keysPressed.S || keysPressed.s; } +function movingLeft() { return keysPressed.ArrowLeft || keysPressed.A || keysPressed.a; } +function movingRight() { return keysPressed.ArrowRight || keysPressed.D || keysPressed.d; } + +function startGame() { + gameStopped = false; + rotationStarted = false; + rotationComplete = false; + + player = new Player(arenaSize / 2, arenaSize / 2); + clocks = getClockPositions(randomLayout); +} + +function stopGame() { + gameStopped = true; +} + +function generateNewLayout() { + let newLayout; + do { + newLayout = Math.floor(Math.random() * 5); + } while (newLayout === randomLayout); + randomLayout = newLayout; +} + + +function keydownHandler(event) { + if (keysPressed.hasOwnProperty(event.key)) { + keysPressed[event.key] = true; + } +} + +function keyupHandler(event) { + if (keysPressed.hasOwnProperty(event.key)) { + keysPressed[event.key] = false; + } +} + +document.addEventListener('keydown', keydownHandler); +document.addEventListener('keyup', keyupHandler); + +document.getElementById("resetButton").addEventListener("click", function() { + stopGame(); + resetRotation(); + generateNewLayout(); + startGame(); +}); + +document.getElementById("retryButton").addEventListener("click", function() { + stopGame(); + resetRotation(); + startGame(); +}); + +startGame(); +gameLoop(); diff --git a/Expedience.Web/wwwroot/time-bomb.html b/Expedience.Web/wwwroot/time-bomb.html new file mode 100644 index 0000000..63436b1 --- /dev/null +++ b/Expedience.Web/wwwroot/time-bomb.html @@ -0,0 +1,53 @@ + + + + + + Time Bomb + + + +

AVOID THE TIME BOMBS

+
+

Control the player with WSAD or Arrow keys. The clocks will start ticking as soon as the player moves.

+
+ +
+ + +
+ + +