From cedcaaea0752918835e8aa8b6865359e4fae5aaa Mon Sep 17 00:00:00 2001 From: Bye <bye@byecorps.com> Date: Thu, 8 Sep 2022 21:23:28 +0100 Subject: [PATCH] updatre --- ...rite-0002.aseprite => Sprite-0002.aseprite | Bin ...rite-0003.aseprite => Sprite-0003.aseprite | Bin ...rite-0004.aseprite => Sprite-0004.aseprite | Bin ...rite-0005.aseprite => Sprite-0005.aseprite | Bin ...rite-0006.aseprite => Sprite-0006.aseprite | Bin ...aimerthing.aseprite => aimerthing.aseprite | Bin ...seprite => boringaptwalltopcorner.aseprite | Bin ..._a.aseprite => crazyabackground_a.aseprite | Bin game.js | 273 ++++++++++++------ assets/hamster.aseprite => hamster.aseprite | Bin .../hamsterx2.aseprite => hamsterx2.aseprite | Bin letters.js | 5 +- t.aseprite | Bin 3880 -> 4452 bytes t.png | Bin 2095 -> 2459 bytes assets/testbanner.png => testbanner.png | Bin 15 files changed, 189 insertions(+), 89 deletions(-) rename assets/Sprite-0002.aseprite => Sprite-0002.aseprite (100%) rename assets/Sprite-0003.aseprite => Sprite-0003.aseprite (100%) rename assets/Sprite-0004.aseprite => Sprite-0004.aseprite (100%) rename assets/Sprite-0005.aseprite => Sprite-0005.aseprite (100%) rename assets/Sprite-0006.aseprite => Sprite-0006.aseprite (100%) rename assets/aimerthing.aseprite => aimerthing.aseprite (100%) rename assets/boringaptwalltopcorner.aseprite => boringaptwalltopcorner.aseprite (100%) rename assets/crazyabackground_a.aseprite => crazyabackground_a.aseprite (100%) rename assets/hamster.aseprite => hamster.aseprite (100%) rename assets/hamsterx2.aseprite => hamsterx2.aseprite (100%) rename assets/testbanner.png => testbanner.png (100%) diff --git a/assets/Sprite-0002.aseprite b/Sprite-0002.aseprite similarity index 100% rename from assets/Sprite-0002.aseprite rename to Sprite-0002.aseprite diff --git a/assets/Sprite-0003.aseprite b/Sprite-0003.aseprite similarity index 100% rename from assets/Sprite-0003.aseprite rename to Sprite-0003.aseprite diff --git a/assets/Sprite-0004.aseprite b/Sprite-0004.aseprite similarity index 100% rename from assets/Sprite-0004.aseprite rename to Sprite-0004.aseprite diff --git a/assets/Sprite-0005.aseprite b/Sprite-0005.aseprite similarity index 100% rename from assets/Sprite-0005.aseprite rename to Sprite-0005.aseprite diff --git a/assets/Sprite-0006.aseprite b/Sprite-0006.aseprite similarity index 100% rename from assets/Sprite-0006.aseprite rename to Sprite-0006.aseprite diff --git a/assets/aimerthing.aseprite b/aimerthing.aseprite similarity index 100% rename from assets/aimerthing.aseprite rename to aimerthing.aseprite diff --git a/assets/boringaptwalltopcorner.aseprite b/boringaptwalltopcorner.aseprite similarity index 100% rename from assets/boringaptwalltopcorner.aseprite rename to boringaptwalltopcorner.aseprite diff --git a/assets/crazyabackground_a.aseprite b/crazyabackground_a.aseprite similarity index 100% rename from assets/crazyabackground_a.aseprite rename to crazyabackground_a.aseprite diff --git a/game.js b/game.js index 7e35532..b26a15c 100644 --- a/game.js +++ b/game.js @@ -300,7 +300,7 @@ class Room { } start() { - + c.camera = {x:0,y:0} } } @@ -317,12 +317,19 @@ c.fill("#151f1f"); gameCtx.imageSmoothingEnabled = false; let dPM = _=>{ - // r = Room c.ctx.globalAlpha = .7; c.fill("#000"); + c.ctx.globalAlpha = .85; + c.drawRect((c.w/2-150)+c.camera.x,120+c.camera.y, 300,120, "#000") c.ctx.globalAlpha = 1; - c.dT("paused", c.w/2+c.camera.x,(c.h/2-25)+c.camera.y, 1,1,"#fff", "middle","middle"); - + c.dT("paused", c.w/2+c.camera.x,c.h/2-30+c.camera.y, 3,3,"#fff", "middle","middle"); + for (let o in gameRoom.pseo) { + let t = c.dT(gameRoom.pseo[o].t, c.w/2+c.camera.x, c.h/2+(o*9)+c.camera.y, 1,1,"#fff", "middle"); + if (o == gameRoom.pses) { + let a = images.ui.a; + c.drawImg(a, ((c.w/2+c.camera.x)-t.w/2)-8, c.h/2+(o*9)+c.camera.y, 6,7) + } + } } c.dT("Death By Hamster", c.w / 2, c.h / 2 - 40, 2, 2, "white", "middle"); @@ -405,12 +412,12 @@ for (var key in images) { let levels = [ { - "name": "Tutorial", - "data": "NrCMBoIJgXXNLgMx2AVkQFlRXqrgAMy+R4s8BxoqmZhqGED8GVjyRHdL64AbF1bgMvDAHYhwQQA4p0EvAhJFCOiiUDVESRoSSK+yDi01NbE4MMQMem8c10za8qgCc5V5pXXOzhc6SxNjCBCEIcnbgcuE64LHgHglyaCYeqZqSGQgeUXK+ggmgwSbFIqXE-GkCJnJV8IKC2YKS9dIi8ahZ5fCtNb3RDu0egYle7WWGkmV6tp3CdOEEkuIcoMprBHqCoCp6BLvzwAcq2Sf9x5Aqq5RX0aSHbqSSMs+JXeDTvHKHxZuQYkguEBZT+whGgJ+vAgclemjkT3hn2qNwQZVRuB0pQgbUxRzx2VwYVKBFxkAIhPJyM0oAIcLRBERaIgTLx9NBxHZYy5HlZAPeMCAA" + name: "Tutorial", + data: "NrCMBoIJgXXNLgMx2AVkQFlRXqrgAMy+R4s8BxoqmZhqGED8GVjyRHdL64AbF1bgMvDAHYhwQQA4p0EvAhJFCOiiUDVESRoSSK+yDi01NbE4MMQMem8c10za8qgCc5V5pXXOzhc6SxNjCBCEIcnbgcuE64LHgHglyaCYeqZqSGQgeUXK+ggmgwSbFIqXE-GkCJnJV8IKC2YKS9dIi8ahZ5fCtNb3RDu0egYle7WWGkmV6tp3CdOEEkuIcoMprBHqCoCp6BLvzwAcq2Sf9x5Aqq5RX0aSHbqSSMs+JXeDTvHKHxZuQYkguEBZT+whGgJ+vAgclemjkT3hn2qNwQZVRuB0pQgbUxRzx2VwYVKBFxkAIhPJyM0oAIcLRBERaIgTLx9NBxHZYy5HlZAPeMCAA" }, { - "name": "First Floor", - "data": "NrCMBoNBddgFnABmbYBWSq6YEzY3AGYCA2cRJNAdgsjU0xjlH3OcNo8wA56dwATn6FQKbpCgNI+CaBJzEcptNDs07Lmlm1c2yLSL7QteMdro0w-Hrj5x+lLeD3ijiu8t3k4UjR-UquCBAvghhCThmIhR4JjhoBDksbSxfLFiwdLCGWGqEBmRqvGqMarJaHwmWd7VfizgsmgQCs3EjW0QzlBuDfhGDSQDYCTk3eB8Zg2Z9WCSHtMk46DRzeIkXmAowpuZiXGd-Z0ow1CIuzIHDXyzBb5tiLdYU3P4LxCI73SndON4bZgfqtpigLpkvsJ3ttenNDG0+D9dPCOg1rMcUXN9t1xC1Dhi7hc+BdyD9yF9TG1yJtRvcGpgnucAVc5iQvhs2qM2mwucyemDaXNhE99sLuYsBWcJZcvvIFltIENOogfqBPkr8ZB-tNAZ1GLqpWoDVTOmSTTCoGNoNAgA" + name: "First Floor", + data:"NrCsBoGZwBgXXMALLWCzgIxsRATDsAJzgF7qYEry7gQ0YBshEA7C+ABwckMSYwOmbAwKDMFKFknRyibAQnys05QvRVVwTXO2pdzSoQFZBuxYo1YyVo5EnCoD9WuxLgb8ZJLvsJXX5OaoL2ytyhHuDsEdgQMeDM8fiS7AFcpBQozGn8koYpWtjcvlLxBPHYEWJBeigR7ChI6BAQTbTMbRgNzemdECR9puCDjp2ORp2G0J2K0KC20IxWgjOo84jVSxtY0Kw9Rpz7RfuCh7QkZxjcl2xcPRA3Ko8EjyiP0I-Ml8Uf6OynyyiMmGkioDhBajooKhyjmkhQ60ii3hCRRe1hQOUBHRkQIW0i2HxbkxkRaeRhkXYiKKFMC1KG9McjIIzNRamxzhJni5QyJ4BIfJIOJpwvSfPY4p5zGFgmYfIg8q5KEcSzgQA" }, ] @@ -507,16 +514,13 @@ var levelRef = { "x": 416, "type": "wall" }, + { + "x": 448, + "type": "vent" + }, ] } -var humanRef = { - "file": images.level.human, - "normie": { - "x": 0, "y": 0, "w": 32, "h": 32 - } -} - for (let tile of levelRef.tiles) { // if the tile is missing properties from the default, add them @@ -610,18 +614,24 @@ menu.keyDown = (key) => { } var gameRoom = new Room("Game"); +let lvlS = new Room("Level Select") +lvlS.s = 0 +lvlS.o = levels gameRoom.humans = 0 var player = new Entity("Player", 0,0); player.speed = 0; player.maxSpeed = 20; player.direction = 0; -player.accel = 1.5; player.sprite = images.player.car; player.crop = hamsterRef.nl; player.x = 0; player.y = 0; player.w = player.crop.w*2; player.h = player.crop.h*2; +gameRoom.o = [{t:"Next Level",a:_=>{lvlS.s += 1; lvlS.keyDown("Space"); gameRoom.tutorial=0}}, {t:"Level Select",a:_=>{setRoom(4)}}, {t:"Menu", a:_=>{setRoom(1)}}] +gameRoom.s = 0 +gameRoom.pseo = [{t:"Back to Menu", a:_=>{setRoom(1)}},{t:"Level Select",a:_=>{setRoom(4)}}] +gameRoom.pses = 0 player.oldDir = 0; player.step = _=> { @@ -630,33 +640,43 @@ player.step = _=> { player.y += player.speed * Math.sin(player.direction * pi / 180); // check that the player won't go into a wall on the next step, and if so, stop. player.checkpoints = []; - for (let i = 0; i < 6; i++) { + for (let i = 0; i < 9; i++) { let carCx = player.x + player.w/2; let carCy = player.y + player.h/2; let pointOx = 0; let pointOy = 0; - if (i==0) { - pointOx = -32; - } else if (i==1) { - pointOx = 32; - } else if (i==2){ - pointOx = -30; - pointOy = -15; - } else if (i==3){ - pointOx = -30; - pointOy = 15; - } else if (i==4){ - pointOx = 30; - pointOy = -15; - } else if (i==5){ - pointOx = 30; - pointOy = 15; + switch (i) { + case 0: + pointOx = -32; + break; + case 1: + pointOx = 32; + break; + case 2: + pointOx = -30; + pointOy = -15; + break; + case 3: + pointOx = -30; + pointOy = 15; + break; + case 4: + pointOx = 30; + pointOy = -15; + break; + case 5: + pointOx = 30; + pointOy = 15; + break; + case 6: + pointOx = 20; + break; + case 7: + pointOx = -20; } - - // get gunx and guny by moving backwards (gunOx and gunOy) from the center of the car in this.direction let pointX = carCx - pointOx * Math.cos(player.direction * pi / 180) - pointOy * Math.sin(player.direction * pi / 180); let pointY = carCy - pointOx * Math.sin(player.direction * pi / 180) + pointOy * Math.cos(player.direction * pi / 180); @@ -670,19 +690,13 @@ player.step = _=> { let x = checkpoint.x / 64; let y = checkpoint.y / 64; if (gameRoom.checkwall(x, y)) { - checkpoint.stuck = true; + player.direction = player.oldDir; + player.x = player.xy[0]; + player.y = player.xy[1]; + player.speed -= 0.1; } } - if (player.checkpoints[0].stuck || player.checkpoints[1].stuck) { - // move down sideways if stuck - player.direction = player.oldDir; - player.x = player.xy[0]; - player.y = player.xy[1]; - player.speed -= 0.1; - } - - // keep the camera centered on the player c.setCamera(player.x - c.w/2, player.y - c.h/2); @@ -712,8 +726,12 @@ player.draw = _=> { player.gx = gunx player.gy = guny - // get the angle between the gun and the mouse - player.aim = Math.atan2(c.mousePos.y - guny, c.mousePos.x - gunx) * 180 / pi; + if (!pause&&!gameRoom.finish) { + + // get the angle between the gun and the mouse + player.aim = Math.atan2(c.mousePos.y - guny, c.mousePos.x - gunx) * 180 / pi; + + } // canvas.drawText(`Width${gun.width} Height${gun.height}`, gunx, guny-15, 1, 1, "green", "middle", "middle"); c.drawImg(gun, gunx, guny, gun.width*2, gun.height*2, player.aim, gunx, guny); // these two vars at the end are where the gun's center is placed @@ -724,8 +742,6 @@ player.draw = _=> { } - c.dT(`${Math.round(player.speed*100000)/100000}` ,player.x, player.y, 1,1, "white") - } player.shoot = () => { @@ -766,10 +782,9 @@ player.shoot = () => { } -gameRoom.spawn(player); gameRoom.keyDown = (key) => { - if (!pause){ + if (!pause&&!gameRoom.finish){ if (key == "ArrowUp" || key == "KeyW") { player.speed += player.accel; if (player.speed > player.maxSpeed) { @@ -798,41 +813,100 @@ gameRoom.keyDown = (key) => { player.shoot(); } + if (key == "ShiftLeft") { + console.log("Triggered!") + for (let i = 6; i < 9; i++) { + let x = Math.floor(player.checkpoints[i].x / 64); + let y = Math.floor(player.checkpoints[i].y / 64); + + for (let tile of gameRoom.level) { + if (levelRef.tiles[tile[0]].type == "vent" && tile[1] == x && tile[2] == y) { + for (let tile of gameRoom.level) if (levelRef.tiles[tile[0]].type == "vent" && !(tile[1] == x) && !(tile[2] == y)) { + player.x = tile[1]*64; + player.y = tile[2]*64+16; + player.speed = 0; + } + } + } + + } + } + } - if (key == "KeyP"||key=="Escape") { - pause = !pause + if (!gameRoom.finish ) { + if (key == "KeyP" || key == "Escape") { + pause = !pause + } + if (pause) { + if (key == "ArrowUp"||key == "KeyW") { + gameRoom.pses -= 1 + if (gameRoom.pses < 0) { + gameRoom.pses = gameRoom.pseo.length-1 + } + } + if (key == "ArrowDown"||key == "KeyS") { + gameRoom.pses += 1 + if (gameRoom.pses > gameRoom.pseo.length-1) { + gameRoom.pses = 0 + } + } + if (key == "Space" || key == "Enter") { + pause = 0; + gameRoom.tutorial = 0; + gameRoom.pseo[gameRoom.pses].a() + } + } + } + if (gameRoom.finish) { + if (key == "ArrowUp"||key == "KeyW") { + gameRoom.s -= 1 + if (gameRoom.s < 0) { + gameRoom.s = gameRoom.o.length-1 + } + } + if (key == "ArrowDown"||key == "KeyS") { + gameRoom.s += 1 + if (gameRoom.s > gameRoom.o.length-1) { + gameRoom.s = 0 + } + } + if (key == "Space" || key == "Enter") { + gameRoom.finish = 0; + gameRoom.tutorial = 0; + gameRoom.o[gameRoom.s].a(); + } } } gameRoom.keyHeld = (key) => { - if (!pause){ - if (key == "ArrowUp" || key == "KeyW") { - player.speed += player.accel*2.5; - if (player.speed > player.maxSpeed) { - player.speed = player.maxSpeed; + if (!pause&&!gameRoom.finish){ + if (key == "ArrowUp" || key == "KeyW") { + player.speed += player.accel; + if (player.speed > player.maxSpeed) { + player.speed = player.maxSpeed; + } } - } - if (key == "ArrowDown" || key == "KeyS") { - player.speed -= player.accel*1.53; - if (player.speed < -player.maxSpeed) { - player.speed = -player.maxSpeed; + if (key == "ArrowDown" || key == "KeyS") { + player.speed -= player.accel*1.1; + if (player.speed < -player.maxSpeed) { + player.speed = -player.maxSpeed; + } } - } - if (key == "ArrowLeft" || key == "KeyA") { - player.direction -= 2.5; - if (player.direction < 0) { - player.direction = 360; + if (key == "ArrowLeft" || key == "KeyA") { + player.direction -= 2.5; + if (player.direction < 0) { + player.direction = 360; + } } - } - if (key == "ArrowRight" || key == "KeyD") { - player.direction += 2.5; - if (player.direction > 360) { - player.direction = 0; + if (key == "ArrowRight" || key == "KeyD") { + player.direction += 2.5; + if (player.direction > 360) { + player.direction = 0; + } } } - } } gameRoom.click = (e) => { - if (!pause){ + if (!pause&&!gameRoom.finish){ player.shoot(); } } @@ -852,9 +926,20 @@ gameRoom.start = () =>{ gameRoom.level = customLv } gameRoom.level = JSON.parse(lzs.decompressFromEncodedURIComponent(gameRoom.level)) + gameRoom.finish = 0; + + gameRoom.objects = []; + + + gameRoom.humans = 0; + gameRoom.spawn(player); + if (gameRoom.tutorial) { - player.accel = .5 + player.accel = .8 + } else { + player.accel = 1.5 + } @@ -916,9 +1001,10 @@ gameRoom.start = () =>{ } gameRoom.step = _=> { - if (!pause) { + if (!pause&&!gameRoom.finish) { if (gameRoom.humans <= 0){ - pause = true; + gameRoom.tutorial = 0; + gameRoom.finish = true; } // step all objects in the room for (let obj of gameRoom.objects) { @@ -927,7 +1013,7 @@ gameRoom.step = _=> { } } -gameRoom.draw = () => { +gameRoom.draw = _=> { for (let tile of gameRoom.level) { // [index, x, y] @@ -958,6 +1044,23 @@ gameRoom.drawGUI = _=>{ if (pause) { dPM(gameRoom); } + if (gameRoom.finish) { + c.ctx.globalAlpha = .7; + c.fill("#000"); + c.ctx.globalAlpha = .85; + c.drawRect((c.w/2-150)+c.camera.x,120+c.camera.y, 300,150, "#000") + c.ctx.globalAlpha = 1; + + + c.dT("You Won!", c.w/2+c.camera.x,c.h/2-30+c.camera.y, 3,3,"#fff", "middle","middle"); + for (let o in gameRoom.o) { + let t = c.dT(gameRoom.o[o].t, c.w/2+c.camera.x, c.h/2+(o*9)+c.camera.y, 1,1,"#fff", "middle"); + if (o == gameRoom.s) { + let a = images.ui.a; + c.drawImg(a, ((c.w/2+c.camera.x)-t.w/2)-8, c.h/2+(o*9)+c.camera.y, 6,7) + } + } + } } let editor = new Room("Editor"); @@ -1063,9 +1166,7 @@ editor.drawGUI = _=>{ c.sliceImage(editor.t.file, c.mousePos.x+16,c.mousePos.y+16,32,32,32*editor.i,0,32,32); } -let lvlS = new Room("Level Select") -lvlS.s = 0 -lvlS.o = levels + lvlS.drawGUI = () => { c.dT("Death by Hamster", c.w/2, 25, 2, 2, "white", "middle", "top"); @@ -1086,7 +1187,7 @@ lvlS.keyDown = (key) => { lvlS.s = lvlS.o.length-1 } } - if (key == "ArrowDown" ||key=="ArrowLeft"||key == "KeyW"|| key == "KeyA") { + if (key == "ArrowDown" ||key=="ArrowLeft"||key == "KeyS"|| key == "KeyA") { lvlS.s += 1 if (lvlS.s > lvlS.o.length-1) { lvlS.s = 0 @@ -1100,7 +1201,7 @@ lvlS.keyDown = (key) => { setRoom(2) } if (key == "KeyE") { - editor.l = JSON.parse(lvlS.o[lvlS.s].data); + editor.l = JSON.parse(lzs.decompressFromEncodedURIComponent(lvlS.o[lvlS.s].data)); setRoom(3) } } @@ -1110,7 +1211,7 @@ options.s = 0 options.ops = o; options.o = [{ "t": "Show FPS", - "a": _=>{ o.showFPS = !o.showFPS }, + "a": _=>{ o.showFPS = !o.showFPS; localStorage.setItem('dbh_showFPS', o.showFPS); }, "v": "showFPS" }, { "t": "Menu", @@ -1141,7 +1242,7 @@ options.keyDown = (key) => { options.s = options.o.length-1 } } - if (key == "ArrowDown" ||key=="ArrowLeft"||key == "KeyW") { + if (key == "ArrowDown" ||key=="ArrowLeft"||key == "KeyS") { options.s += 1 if (options.s > options.o.length-1) { options.s = 0 diff --git a/assets/hamster.aseprite b/hamster.aseprite similarity index 100% rename from assets/hamster.aseprite rename to hamster.aseprite diff --git a/assets/hamsterx2.aseprite b/hamsterx2.aseprite similarity index 100% rename from assets/hamsterx2.aseprite rename to hamsterx2.aseprite diff --git a/letters.js b/letters.js index a0b8d2f..9b0347c 100644 --- a/letters.js +++ b/letters.js @@ -91,13 +91,12 @@ const fntH = [ ]; const fntI = [ - + [, 1, 1, 1, 1, 1, 1], [, 1, 1, 1, 1, 1, 1], [, , , 1, 1], [, , , 1, 1], [, , , 1, 1], - [, , , 1, 1], - [, , , 1, 1], + [, 1, 1, 1, 1, 1, 1], [, 1, 1, 1, 1, 1, 1], ]; diff --git a/t.aseprite b/t.aseprite index 88533f274d87786feac38b38e560a1341ee21ea1..49c5cac42282238b3105fdb4626a8c162e729241 100644 GIT binary patch delta 553 zcmZ1>_e4oCMUa8v!BPPR1u$e}U|>jLKn6Aw72_EXPi$1>Z(?L%kW{dNN?LE+n$9T3 zz>t=f_Md@a%9JVp_c1UqurM$(FxX61V_L_SmX<bU%9PDlm_m4Xtr-~@etl#D8_hJ? zj@hRE69WSSs{#`P69YTLe<rAz3^EMD3>9<sUb@c3qR7K~;phM7FZX``*SuR{N5&#Q zv6Pn2LPa|TN@Tv-=~r&%-uZs^n_YTqKijCTTDP31M5g`PzV{q&7N@w}-j=!cb=Ltq zFSji^-Pdf+bG&)Hd)xmb7JWQ({4c9KthPFAP@nO&wjZS8O6lCN+uOh@-bY?HWq$5^ zS)nF;=_;+on{-q+^iL8LbxoY|d?(AdkG8kJ`&^#V_*v()z`oU!R5}A?jtkvzKiRQJ z!-(r}0Ml;`$v^=Xw|(!uL$=qQ>aaO)Ton8L__z5#CVx)5E}r$s)#E(_A`loMp|A`Z z3d{@~NTDFbAjnWLCpm$k`M=4r1nYHse)|XWY<_>HJKp4|L3N+qFRx&p&FSyw@`JeB zVyk(NUFoYYDZU<C;RcfUxw0K3@l1Xv^RX*@<|W1HZ+82EBp&s7gCwlX=dvbewM*~X tsqUNYcR}Rvo0U;zGnPrD`G0Jx1u58+{&tC`+dAot*#8U+R`<i+0|1v(*rxyh delta 86 zcmaE&v_ei%gP(!n!BRd31u$e}U|>jLKn7M572_E<PHa@=FJok2kW{dON?LB*n$F0z XgMop8g@KWQ!D_M*)4I*im_m2}XDAOM diff --git a/t.png b/t.png index 63a041b1c6a136ddcd52123eea4325d9387ec85d..6cd7464770bed96fafcc11f79eee590ed3d0be6d 100644 GIT binary patch literal 2459 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 Z@!_h($fG-Ftp>HdJYD@<);T3K0RVhz&U*j= literal 2095 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 diff --git a/assets/testbanner.png b/testbanner.png similarity index 100% rename from assets/testbanner.png rename to testbanner.png -- GitLab