function createCanvas(id) { var tempCanvas = document.createElement("canvas"); tempCanvas.id = id; tempCanvas.width = canvases.cvs.width; tempCanvas.height = canvases.cvs.height; tempCanvas.style = "image-rendering:pixelated;image-rendering: crisp-edges;display:none";//display:none; document.body.appendChild(tempCanvas); canvases[`${id}cvs`] = document.getElementById(id); canvases[`${id}ctx`] = canvases[`${id}cvs`].getContext("2d"); } function startLoops() { try {draw} catch (err){console.warn(bug+" no draw function found");return null;} try {update} catch (err){console.warn(bug+" no update function found");return null;} try {input} catch (err){seperateInputLoop=false;} onAssetsLoaded(); requestAnimationFrame(drawLoop); setInterval(updateLoop,1000/updateFPS); if(seperateInputLoop) { setInterval(inputLoop,4); } } function mousePosition() { if(drawMode===0) { return {x:(mousePos.x)-camera.x,y:(mousePos.y)-camera.y}; } else if(drawMode===1) { var xoff = canvases.cvs.width/2; var yoff = canvases.cvs.height/2; return {x:((mousePos.x-xoff)/camera.zoom+xoff)-camera.x,y:((mousePos.y-yoff)/camera.zoom+yoff)-camera.y}; } else { var xoff = canvases.cvs.width/2; var yoff = canvases.cvs.height/2; var tempPos = {x:((mousePos.x-xoff)/camera.zoom+xoff)-camera.x,y:((mousePos.y-yoff)/camera.zoom+yoff)-camera.y}; var center = {x:-camera.x + cw/2, y:-camera.y + ch/2}; var tempAngle = pointTo(center,tempPos) - camera.angle; var tempDist = dist(center,tempPos); return {x:center.x + (Math.cos(tempAngle) * tempDist),y:center.y + (Math.sin(tempAngle) * tempDist)} } } function addStyle() { var tempStyle = document.createElement("style"); tempStyle.id="gamejsstyle"; document.head.appendChild(tempStyle); var tempMeta = document.createElement("meta"); tempMeta.setAttribute("charset","utf-8"); document.head.appendChild(tempMeta); } function rand(min,max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function radToDeg(rad) {return rad / Math.PI * 180;} function degToRad(deg) {return deg * Math.PI / 180;} function velocity(angle) { return {x:Math.sin(angle),y:Math.cos(angle)}; } function pointTo(point,targetPoint) { var adjacent = (targetPoint.x - point.x); var opposite = (targetPoint.y - point.y); var h = Math.atan2(opposite, adjacent); return h; } function loadImagesAndSounds() { var curpath=""; context = new AudioContext(); sfxVolumeNode = context.createGain(); sfxVolumeNode.connect(context.destination); bmgVolumeNode = context.createGain(); bmgVolumeNode.connect(context.destination); deeper(images,"image"); deeper(audio,"sound"); function deeper(curpos,type) { let addedPath=""; for(let j=0;j<curpos.length;j++) { if(typeof curpos[j]=="string") { if(j==0) { curpath+=curpos[j]; addedPath = curpos[j]; } else { if(type=="image") { let name = curpath + curpos[j]; imagePaths.push(name); let temp = new Image(); temp.src = name; temp.onerror = function () { console.warn(bug+" "+this.src + " was not found"); }; temp.onload = function() {spriteLoad(name,temp);} imgs.push(temp); } else if(type=="sound") { audioPaths.push(curpath + curpos[j]); newSound(curpath + curpos[j]); } } } if(typeof curpos[j]=="object") { deeper(curpos[j],type); } } curpath = curpath.slice(0,curpath.length-addedPath.length); } loadingCircle = new Image(); loadingCircle.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAf0lEQVQ4jc2SuxHAIAxDbY4pMpHmyJSeKGuQKpyj2Hy6qETPQvhQCWRmDRfeZ4cJAGW28mAUyL4Pqmx2nfK+zaR59glRHo5qZi0BaPHmbDhiyuzdsza9wcrtEVtG4Ip+FLCzTM+WneWxPv9gpQUzmhncLPOHUCYfHr4/C4r2dQPfhkeIbjeYWgAAAABJRU5ErkJggg=="; clickSound = new Audio("data:audio/x-wav;base64,UklGRowBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YWgBAADa/6T/2/+S/x//pP769Xr4fPh5+H34evh7+Pv6gf18/QIAhQcIDxUZFR4VHhgeEx6TFgkPCgqAAnz49/X18HLu7ubo4eXc5dzj3Gjf5+Fr5G7pce759YECiwwRFBQZlxuXG5cbFRmTFo8RCAoAAPz69/X08G7udO5s7vz1dvj++nv9gP3+/wEAgwKBAogHkBEpLUNG1lzqcPV683r4eu51ZmnVV7w+qypy88fDoKAXlAqKBoUAgIeHlpYsrTu87Ot9/ZIRGxkoKDEtqionIxgZiwfj5lnaz9JN0E3QV9pd3+Tm9fUBAAQFBwWNDBEPFxSYFpsWEA9+/fP1dfNt83bz8fX9+gQFkAwPDxQPEQ+IBwIAdfjv8OLm2+HX3Nrh4+bm63n4BAUUDx0ZqyCrJSkelBGEAnb4a+5a5Njcztxc31jkZunx9QgFGhQfGa0gqCCuIKkgpRsaFIsHdfhj6dfh0NzM19Th6es="); let pos = {x:cw/2-100,y:ch/2-100}; optionsButtons.screenSize = {x:pos.x+160,y:pos.y+12,w:50,h:20}; optionsButtons.sfx = {x:pos.x+125,y:pos.y+40,w:120,h:20}; optionsButtons.bmg = {x:pos.x+125,y:pos.y+70,w:120,h:20}; loadLoop(); } function loadLoop() { if(Object.keys(sprites).length == imagePaths.length && audioPaths.length == audioLoadedLength) { startLoops(); imagePaths=[]; audioPaths=[]; imgs=[]; } else { curCtx.fillStyle="#2d2d2d"; curCtx.fillRect(0,0,cw,ch); text(`audio: ${audioLoadedLength}/${audioPaths.length}`,10,30,"white",2); text(`sprites: ${Object.keys(sprites).length}/${imagePaths.length}`,10,10,"white",2); curCtx.setTransform(1, 0, 0, 1, Math.round(cw/2), Math.round(ch/2)); curCtx.rotate(loadAng); loadAng+=0.1; curCtx.drawImage(loadingCircle,Math.round(-8),Math.round(-8)); curCtx.setTransform(1, 0, 0, 1, 0, 0); requestAnimationFrame(loadLoop); } } function spriteLoad(path,image) { let startpos; let endpos = path.lastIndexOf("."); for(let j=endpos-1;acceptableChars.includes(path[j]);j--) {startpos=j;} let spriteName = path.slice(startpos,endpos) let dsize=Math.max(image.width,image.height)/2; sprites[spriteName] = {spr:image,drawLimitSize:dsize}; } function newSound(src) { let startpos; let endpos = src.lastIndexOf("."); for(let j=endpos-1;acceptableChars.includes(src[j]);j--) {startpos=j;} let soundName = src.slice(startpos,endpos); sounds[soundName] = {nodes:[],volNodes:[],src:src,type:"sfx",volume:1}; sounds[soundName].nodes = [1]; let loadingSound = new Audio(); loadingSound.onerror = function () { console.warn(bug+" "+ src + " was not found"); }; loadingSound.src = src; loadingSound.preload='auto'; loadingSound.addEventListener('canplaythrough', function() { audioLoadedLength++; }, false); sounds[soundName].nodes.push(loadingSound); let soundNode = context.createMediaElementSource(loadingSound); let gainNode = context.createGain(); soundNode.connect(gainNode); gainNode.connect(sfxVolumeNode); abuffer.push(soundNode); volumeList.push(gainNode); sounds[soundName].volNodes.push(volumeList.length-1); } function addSound(sound) { let loadingSound = new Audio(); loadingSound.src = sound.src; loadingSound.preload='auto'; sound.nodes.splice(sound.nodes[0],0,loadingSound); let soundNode = context.createMediaElementSource(loadingSound); let gainNode = context.createGain(); gainNode.gain.value=sound.volume; soundNode.connect(gainNode); gainNode.connect(sound.type=="sfx"?sfxVolumeNode:bmgVolumeNode); abuffer.push(soundNode); volumeList.push(gainNode); sound.volNodes.push(volumeList.length-1); volumeList[sound.volNodes[sound.volNodes.length-1]].gain.value = volumeList[sound.volNodes[0]].gain.value } function play(sound) { s=sound.nodes; if(s[s[0]].ended || !(s[s[0]].played.length)) { s[s[0]].play(); s[0]++; if(s[0]==s.length) { s[0]=1; } } else { addSound(sound); s[s[0]].play(); s[0]++; if(s[0]==s.length) { s[0]=1; } } } function setVolume(sound,volume) { for(let i=0,l=sound.volNodes.length;i<l;i++) { volumeList[sound.volNodes[i]].gain.value = volume; } } function setType(sound,newType) { for(let i=0,l=sound.volNodes.length;i<l;i++) { volumeList[sound.volNodes[i]].disconnect(sound.type=="sfx"?sfxVolumeNode:bmgVolumeNode); volumeList[sound.volNodes[i]].connect(newType=="sfx"?sfxVolumeNode:bmgVolumeNode); } sound.type = newType; } function stop(sound) { s=sound.nodes; for(let i=1;i<s.length;i++) { s[i].pause(); s[i].currentTime = 0; } } function handleOptionsInput() { let ImTierdMakemenuwork=true; if(optionsMenu) { if(mousePress[0]) { if(rectpoint(optionsButtons.screenSize,mousePos)) { if(screenSize=="1:1") { screenSize = "fit"; canvasScale=0; } else { screenSize = "1:1"; canvasScale=1; } } if(!rectpoint({x:cw/2,y:ch/2,w:200,h:200},mousePos)) { optionsMenu=false; ImTierdMakemenuwork=false; } } if(mouseDown[0]) { if(rectpoint(optionsButtons.sfx,mousePos)) { volume.sfx = (mousePos.x-(optionsButtons.sfx.x-60))/120; } if(rectpoint(optionsButtons.bmg,mousePos)) { volume.bgm = (mousePos.x-(optionsButtons.bmg.x-60))/120; } } } if(mousePos.x>cw-32&&mousePos.y<32) { if(mousePress[0]&&ImTierdMakemenuwork) { clickSound.play(); paused=true; optionsMenu=!optionsMenu; } optionsHover = 25; } else { optionsHover = 0; } if(mousePos.x<cw-32&&mousePos.x>cw-64&&mousePos.y<32) { pauseHover = 25; if(mousePress[0]) { clickSound.play(); paused=!paused; } } else { pauseHover = 0; } } function addFont() { var tempStyle = document.createElement("style"); tempStyle.innerHTML = ` @font-face { font-family: 'PixelArial11'; src: url("./pixelmix.ttf") format('truetype'); font-weight: 900; font-style: normal; } html {font-family: 'PixelArial11' !important; font-size: 16px;} `; document.head.appendChild(tempStyle); canvases.ctx.textBaseline = "hanging"; canvases.ctx.textAlign = "left"; } var scaleDefault = 1; function img(img,x,y,angle=0,sx=scaleDefault,sy=scaleDefault) { var half = img.drawLimitSize; if((x+half>drawLimitLeft&&x-half<drawLimitRight&&y+half>drawLimitTop&&y-half<drawLimitBottom)||absDraw) { let spr = img.spr; if(angle===0&&sx===1&&sy===1) { curCtx.drawImage(spr,Math.round(x+camera.x+difx-(spr.width/2)),Math.round(y+camera.y+dify-(spr.height/2))); } else { curCtx.setTransform(sx, 0, 0, sy, Math.round(x+camera.x+difx), Math.round(y+camera.y+dify)); curCtx.rotate(angle); curCtx.drawImage(spr,Math.round(-spr.width/2),Math.round(-spr.height/2)); curCtx.setTransform(1, 0, 0, 1, 0, 0); } } } function imgIgnoreCutoff(img,x,y,angle=0,sx=1,sy=1) { let spr = img.spr; if(angle===0&&sx===1&&sy===1) { curCtx.drawImage(spr,Math.round(x+camera.x+difx-(spr.width/2)),Math.round(y+camera.y+dify-(spr.height/2))); } else { curCtx.setTransform(sx, 0, 0, sy, Math.round(x+camera.x+difx), Math.round(y+camera.y+dify)); curCtx.rotate(angle); curCtx.drawImage(spr,Math.round(-spr.width/2),Math.round(-spr.height/2)); curCtx.setTransform(1, 0, 0, 1, 0, 0); } } function rect(x,y,w,h,color) { curCtx.fillStyle = color; curCtx.fillRect(x-(w/2)+camera.x+difx,y-(h/2)+camera.y+dify,w,h); } function cartesianRect(x,y,w,h,color) { curCtx.fillStyle = color; curCtx.fillRect(x+camera.x+difx,y+camera.y+dify,w,h); } function circle(x,y,r,color) { curCtx.beginPath(); curCtx.arc(x+camera.x+difx, y+camera.y+dify, r, 0, 2 * Math.PI, false); curCtx.fillStyle = color; curCtx.fill(); } function line(x1, y1, x2, y2, weight, color) { curCtx.beginPath(); curCtx.strokeStyle = color; curCtx.lineWidth = weight; curCtx.moveTo(x1 + camera.x + difx, y1 + camera.y + dify); curCtx.lineTo(x2 + camera.x + difx , y2 + camera.y + dify); curCtx.stroke(); } function shape(x,y,relitivePoints,color) { x+=camera.x+difx; y+=camera.y+dify; curCtx.fillStyle = color; curCtx.beginPath(); curCtx.moveTo(x+relitivePoints[0].x, y+relitivePoints[0].y); for(let i=1,l=relitivePoints.length;i<l;i++) { curCtx.lineTo(x+relitivePoints[i].x, y+relitivePoints[i].y); } curCtx.fill(); } function text(txt,x,y,color="black",size=1,maxWidth=cw) { txt = txt.toString(); curCtx.fillStyle = color; curCtx.font = `${Math.round(size)*8}px PixelArial11`; //I hate text wrapping now var txtList = txt.split("\n"); //split string on enters for(let i=0;i<txtList.length;i++) { //go through array of strings if(curCtx.measureText(txtList[i]).width>maxWidth) { //if the string is too big, divide up into smaller strings var tempTxt = txtList[i].split(" "); //split into individual words var tempStr=""; //string for measuring size var addAmount=0; //track where in the txtList we are txtList.splice(i,1); //remove the too long string for(let j=0;j<tempTxt.length;j++) { //go through the split up string if(curCtx.measureText(tempStr + tempTxt[j] + " ").width<maxWidth) { //if adding a word doesn't make tempStr too long, add it, other wise, add tempStr to txtList; tempStr += tempTxt[j] + " "; } else { if(j==0) {tempStr+=tempTxt[j];} //if we are here when j is 0, we have one word that is longer then the maxWidth, so we just draw it txtList.splice(i+addAmount,0,tempStr); //put tempStr in txtList addAmount++; //move the position we put the tempStr in tempStr=""; //reset tempStr tempTxt.splice(0,(j==0?1:j)); //delete words that have been used j=-1; //make it so in the next loop, j starts at 0 } } if(tempStr.length!=0) { txtList.splice(i+addAmount,0,tempStr); //add any leftover text } } } for(let i=0;i<txtList.length;i++) { curCtx.fillText(txtList[i],x+camera.x+difx,y+camera.y+dify+((i+(drawMode?1:0))*8*size+(size*i))); } } function textWidth(txt,size=1) { txt = txt.toString(); curCtx.font = `${Math.round(size)*8}px PixelArial11`; return curCtx.measureText(txt).width; } function centerCameraOn(x,y) { camera.x = -x+canvases.cvs.width/2; camera.y = -y+canvases.cvs.height/2; } function moveCamera(x,y) { camera.x -= y * Math.sin(camera.angle); camera.y -= y * Math.cos(camera.angle); camera.x -= x * Math.sin(camera.angle + 1.57079632); camera.y -= x * Math.cos(camera.angle + 1.57079632); } function imgRotScale(x,y,angle,scale,pic,ctx) { //used for camera movement ctx.setTransform(scale, 0, 0, scale, x, y); ctx.rotate(angle); ctx.drawImage(pic,-pic.width/2,-pic.height/2); ctx.setTransform(1, 0, 0, 1, 0, 0); } function drawCursor() { if(cursor.sprite&&mouseOnCanvas) { if(cursor.alignment) { canvases.ctx.drawImage(cursor.sprite.spr,mousePos.x-Math.round(cursor.sprite.spr.width/2),mousePos.y-Math.round(cursor.sprite.spr.height/2)); } else { canvases.ctx.drawImage(cursor.sprite.spr,mousePos.x,mousePos.y); } cursor.show = false; } else { cursor.show = true; } } function render() { if(drawMode===1) { imgRotScale(canvases.cvs.width/2,canvases.cvs.height/2,0,camera.zoom,canvases.buffer1cvs,canvases.ctx); } if(drawMode===2) { imgRotScale(canvases.cvs.width/2,canvases.cvs.height/2,camera.angle,1,canvases.buffer2cvs,canvases.buffer1ctx); imgRotScale(canvases.cvs.width/2,canvases.cvs.height/2,0,camera.zoom,canvases.buffer1cvs,canvases.ctx); } } function clearCanvases() { canvases.ctx.clearRect(0,0,canvases.cvs.width,canvases.cvs.height); canvases.buffer1ctx.clearRect(0,0,canvases.buffer1cvs.width,canvases.buffer1cvs.height); canvases.buffer2ctx.clearRect(0,0,canvases.buffer2cvs.width,canvases.buffer2cvs.height); } function switchDrawMode() { if(camera.zoom<1) {camera.zoom=1;} if(camera.angle!=0) { drawMode=2; } else if(camera.zoom!=1) { drawMode=1; } else { drawMode=0; } switch (drawMode) { case 0: curCtx = canvases.ctx; break; case 1: curCtx = canvases.buffer1ctx; break; case 2: curCtx = canvases.buffer2ctx; break; } } function resizeBuffers() { var tempSize = maxCvsSize/camera.zoom; var tempSizeAndPadding = tempSize + (tempSize/2) canvases.buffer2cvs.width = tempSizeAndPadding; canvases.buffer2cvs.height = tempSizeAndPadding; if(drawMode===2) { difx = (canvases.buffer2cvs.width - canvases.cvs.width)/2; dify = (canvases.buffer2cvs.height - canvases.cvs.height)/2; } else { difx=0; dify=0; } canvases.buffer2ctx.imageSmoothingEnabled = false; } function scaleCanvases() { //scales canvas by canvas scale, if scale is 0, canvas will try to fit screen var style = document.getElementById("gamejsstyle"); if(canvasScale==0) { var tempScale = Math.min(Math.floor(window.innerWidth/canvases.cvs.width),Math.floor(window.innerHeight/canvases.cvs.height)); tempScale=tempScale<1?1:tempScale; autoScale=tempScale; style.innerHTML = `#game {image-rendering:pixelated;image-rendering: crisp-edges;width:${tempScale*canvases.cvs.width}px;cursor: ${cursor.show?"crosshair":"none"};}`; } else { style.innerHTML = `#game {image-rendering:pixelated;image-rendering: crisp-edges;width:${Math.floor(canvasScale*canvases.cvs.width)}px;cursor: ${cursor.show?"crosshair":"none"};}`; } } function drawButtons() { let pos = {x:cw-16,y:16}; //options rect(pos.x,pos.y,34,34,"#9c9c9c"); let c = optionsHover+45; rect(pos.x,pos.y,32,32,`rgb(${c},${c},${c})`); c = optionsHover + 69; let cc = `rgb(${c},${c},${c})`; rect(pos.x,pos.y-6,26,4,cc); rect(pos.x-6,pos.y-6,4,8,cc); rect(pos.x,pos.y+6,26,4,cc); rect(pos.x+11,pos.y+6,4,8,cc); //pause pos.x-=33; rect(pos.x,pos.y,34,34,"#9c9c9c"); c = pauseHover+45; rect(pos.x,pos.y,32,32,`rgb(${c},${c},${c})`); c = pauseHover + 69; cc = `rgb(${c},${c},${c})`; if(paused) { shape(pos.x,pos.y,[{x:-7,y:-10},{x:-7,y:10},{x:10,y:0}],cc); } else { rect(pos.x+6,pos.y,6,20,cc); rect(pos.x-6,pos.y,6,20,cc); } } function drawOptionsMenu() { if(optionsMenu) { let pos = {x:cw/2-100,y:ch/2-100}; rect(cw/2,ch/2,200,200,"#242424"); text("Screen Size:",pos.x+2,pos.y+2,"white",2); let b = optionsButtons.screenSize; rect(b.x,b.y,b.w,b.h,"#444444"); text(screenSize,pos.x+145,pos.y+4,"white",2); text("sfx",pos.x+2,pos.y+30,"white",2); b = optionsButtons.sfx; rect(b.x,b.y,b.w,b.h-10,"#444444"); rect((b.x-60)+(volume.sfx*120),b.y,8,20,"#444444"); text("bgm",pos.x+2,pos.y+60,"white",2); b = optionsButtons.bmg; rect(b.x,b.y,b.w,b.h-10,"#444444"); rect((b.x-60)+(volume.bgm*120),b.y,8,20,"#444444"); } } var k={a:65,b:66,c:67,d:68,e:69,f:70,g:71,h:72,i:73,j:74,k:75,l:76,m:77,n:78,o:79,p:80,q:81,r:82,s:83,t:84,u:85,v:86,w:87,x:88,y:89,z:90,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,BACKTICK:192,MINUS:189,EQUALS:187,OPENSQUARE:219,ENDSQUARE:221,SEMICOLON:186,SINGLEQUOTE:222,BACKSLASH:220,COMMA:188,PERIOD:190,SLASH:191,ENTER:13,BACKSPACE:8,TAB:9,CAPSLOCK:20,SHIFT:16,CONTROL:17,ALT:18,META:91,LEFTBACKSLASH:226,ESCAPE:27,HOME:36,END:35,PAGEUP:33,PAGEDOWN:34,DELETE:46,INSERT:45,PAUSE:19,UP:38,DOWN:40,LEFT:37,RIGHT:39,CONTEXT:93,SPACE:32,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123}; var keyPress = []; var keyDown = []; var mousePress = []; var mouseDown = []; var scroll = 0; var mousePos = { x:0, y:0 } var preventedEvents = [false,true,true]; function addListenersTo(elementToListenTo) { window.addEventListener("keydown",kdown); window.addEventListener("keyup",kup); elementToListenTo.addEventListener("mousedown",mdown); elementToListenTo.addEventListener("mouseup",mup); elementToListenTo.addEventListener("mousemove",mmove); elementToListenTo.addEventListener("contextmenu",cmenu); elementToListenTo.addEventListener("wheel",scrl); } function removeListenersFrom(elementToListenTo) { window.removeEventListener("keydown",kdown); window.removeEventListener("keyup",kup); elementToListenTo.removeEventListener("mousedown",mdown); elementToListenTo.removeEventListener("mouseup",mup); elementToListenTo.removeEventListener("mousemove",mmove); elementToListenTo.removeEventListener("contextmenu",cmenu); elementToListenTo.removeEventListener("wheel",scrl); } function resetInput() { for(var i=0;i<keyPress.length;i++){if(keyPress[i]){keyPress[i]=0}} for(var i=0;i<mousePress.length;i++){if(mousePress[i]){mousePress[i]=0}} scroll=0; } function kdown(e) { var h=e.keyCode; keyPress[h]=keyPress[h]==[][[]]?1:0; keyDown[h]=1; if(preventedEvents[0]) {e.preventDefault()} } function kup(e) { var h=e.keyCode; delete keyPress[h]; delete keyDown[h]; } function mdown(e) { var h=e.button; mousePress[h]=mousePress[h]==[][[]]?1:0; mouseDown[h]=1; if(preventedEvents[1]) {e.preventDefault()} } function mup(e) { var h=e.button; delete mousePress[h]; delete mouseDown[h]; } function mmove(e) { mousePos.x=e.offsetX/(!canvasScale?autoScale:canvasScale); mousePos.y=e.offsetY/(!canvasScale?autoScale:canvasScale); } function cmenu(e) { if(preventedEvents[1]) {e.preventDefault()} } function scrl(e) { scroll+=-1*(e.deltaY/100); if(preventedEvents[2]) {e.preventDefault()} } function dist(point1,point2) { let one = (point2.x - point1.x); let two = (point2.y - point1.y); return Math.sqrt((one*one)+(two*two)); } function circlecircle(circle1,circle2) { if( dist(circle1,circle2) < (circle1.r + circle2.r)) { return true; } else { return false; } } function circlepoint(circle,point) { if( dist(circle,point) < circle.r) { return true; } else { return false; } } function rectrect(rect1,rect2) { if(rect1.x + rect1.w/2 >= rect2.x - rect2.w/2 && rect1.x - rect1.w/2 <= rect2.x + rect2.w/2 && rect1.y + rect1.h/2 >= rect2.y - rect2.h/2 && rect1.y - rect1.h/2 <= rect2.y + rect2.h/2) { return true; } else { return false; } } function rectpoint(rect,point) { if(rect.x + rect.w/2 >= point.x && rect.x - rect.w/2 <= point.x && rect.y + rect.h/2 >= point.y && rect.y - rect.h/2 <= point.y ) { return true; } else { return false; } } function circlerect(circle,rect) { //credit: https://yal.cc/rectangle-circle-intersection-test/ let rectHalfWidth = rect.w/2; let rectHalfHeight = rect.h/2; let deltaX = circle.x - Math.max(rect.x - rectHalfWidth, Math.min(circle.x, rect.x + rectHalfWidth)); let deltaY = circle.y - Math.max(rect.y - rectHalfHeight, Math.min(circle.y, rect.y + rectHalfHeight)); return (deltaX * deltaX + deltaY * deltaY) < (circle.r * circle.r); } function circleOnSideRect(circle,rect) { let rectHalfWidth = rect.w/2; let rectHalfHeight = rect.h/2; let left = rect.x - rectHalfWidth; let right = rect.x + rectHalfWidth; let top = rect.y - rectHalfHeight; let bottom = rect.y + rectHalfHeight; let cx = circle.x; let cy = circle.y; if(cy < top && cx > left && cx < right) { // top side return 0; } else if(cy > bottom && cx > left && cx < right) { // bottom side return 2; } else if (cx < left && cy > top && cy < bottom) { // left side return 3; } else if (cx > right && cy > top && cy < bottom) { // right side return 1; } else { let returnValue=0; // 0 = top, 1 = right, 2 = bottom, 3 = left let topleft = dist (circle,{x:left,y:top}); let topright = dist (circle,{x:right,y:top}); let bottomleft = dist (circle,{x:left,y:bottom}); let bottomright = dist (circle,{x:right,y:bottom}); switch(Math.min(topleft,topright,bottomleft,bottomright)) { // find what corner the cricle is closer to, then determine what side it is closer to case topleft: var m = slope(rect,{x:left,y:top}); var mperp = -(1/m); var b = yIntercept(rect,m); var bperp = yIntercept(circle,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 3;} else {returnValue = 0;} break; case topright: var m = slope(rect,{x:right,y:top}); var mperp = -(1/m); var b = yIntercept(rect,m); var bperp = yIntercept(circle,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 0;} else {returnValue = 1;} break; case bottomleft: var m = slope(rect,{x:left,y:bottom}); var mperp = -(1/m); var b = yIntercept(rect,m); var bperp = yIntercept(circle,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 3;} else {returnValue = 2;} break; case bottomright: var m = slope(rect,{x:right,y:bottom}); var mperp = -(1/m); var b = yIntercept(rect,m); var bperp = yIntercept(circle,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 2;} else {returnValue = 1;} break; } return returnValue; } } function rectOnSideRect(rect1,rect2) { let rectHalfWidth2 = rect2.w/2; let rectHalfHeight2 = rect2.h/2; let left2 = rect2.x - rectHalfWidth2; let right2 = rect2.x + rectHalfWidth2; let top2 = rect2.y - rectHalfHeight2; let bottom2 = rect2.y + rectHalfHeight2; let rectHalfWidth1 = rect1.w/2; let rectHalfHeight1 = rect1.h/2; let rx1 = rect1.x; let ry1 = rect1.y; let left1 = rx1 - rectHalfWidth1; let right1 = rx1 + rectHalfWidth1; let top1 = ry1 - rectHalfHeight1; let bottom1 = ry1 + rectHalfHeight1; // find what point is closer to the rectangle let topleft1 = dist (rect2,{x:left1,y:top1}); let topright1 = dist (rect2,{x:right1,y:top1}); let bottomleft1 = dist (rect2,{x:left1,y:bottom1}); let bottomright1 = dist (rect2,{x:right1,y:bottom1}); let topmiddle1 = dist (rect2,{x:rx1,y:top1}); let rightmiddle1 = dist (rect2,{x:right1,y:ry1}); let bottommiddle1 = dist (rect2,{x:rx1,y:bottom1}); let leftmiddle1 = dist (rect2,{x:left1,y:ry1}); let cx = rx1; let cy = ry1; switch(Math.min(topleft1,topright1,bottomleft1,bottomright1,topmiddle1,rightmiddle1,bottommiddle1,leftmiddle1)) { //set the point we are testing to the closest point to the rectangle case topleft1: cx -= rect1.w/2; cy -= rect1.h/2; break; case topright1: cx += rect1.w/2; cy -= rect1.h/2; break; case bottomleft1: cx -= rect1.w/2; cy += rect1.h/2; break; case bottomright1: cx += rect1.w/2; cy += rect1.h/2; break; case topmiddle1: cy -= rect1.h/2; break; case rightmiddle1: cx += rect1.w/2; break; case bottommiddle1: cy += rect1.h/2; break; case leftmiddle1: cx -= rect1.w/2; break; } if(cy < top2 && cx > left2 && cx < right2) { // top side return 0; } else if(cy > bottom2 && cx > left2 && cx < right2) { // bottom side return 2; } else if (cx < left2 && cy > top2 && cy < bottom2) { // left side return 3; } else if (cx > right2 && cy > top2 && cy < bottom2) { // right side return 1; } else { let returnValue=0; // 0 = top, 1 = right, 2 = bottom, 3 = left let determiningPoint = {x:cx,y:cy}; let topleft = dist (determiningPoint,{x:left2,y:top2}); let topright = dist (determiningPoint,{x:right2,y:top2}); let bottomleft = dist (determiningPoint,{x:left2,y:bottom2}); let bottomright = dist (determiningPoint,{x:right2,y:bottom2}); switch(Math.min(topleft,topright,bottomleft,bottomright)) { // find what corner the point is closer to, then determine what side it is closer to case topleft: var m = slope(rect2,{x:left2,y:top2}); var mperp = -(1/m); var b = yIntercept(rect2,m); var bperp = yIntercept(determiningPoint,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 3;} else {returnValue = 0;} break; case topright: var m = slope(rect2,{x:right2,y:top2}); var mperp = -(1/m); var b = yIntercept(rect2,m); var bperp = yIntercept(determiningPoint,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 0;} else {returnValue = 1;} break; case bottomleft: var m = slope(rect2,{x:left2,y:bottom2}); var mperp = -(1/m); var b = yIntercept(rect2,m); var bperp = yIntercept(determiningPoint,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 3;} else {returnValue = 2;} break; case bottomright: var m = slope(rect2,{x:right2,y:bottom2}); var mperp = -(1/m); var b = yIntercept(rect2,m); var bperp = yIntercept(determiningPoint,mperp); var mid = POI(m,b,mperp,bperp); if(cx<mid) {returnValue = 2;} else {returnValue = 1;} break; } return returnValue; } } function slope(point1,point2) { return ((point2.y-point1.y)/(point2.x-point1.x)); } function yIntercept(point,slope) { return point.y - (slope * point.x); } function POI(m1,b1,m2,b2) { x = (b2 - b1) / (m1 - m2); return x; //y = m1 * x + b1; } function ifRectOnEdgeBounce(rect) { let rx = rect.x; let ry = rect.y; let rw = rect.w/2; let rh = rect.h/2; if(rx+rw>edge.right) { rect.v.x *= -1; rect.x = edge.right-rw; } if(rx-rw<edge.left) { rect.v.x *= -1; rect.x = edge.left+rw; } if(ry+rh>edge.bottom) { rect.v.y *= -1; rect.y = edge.bottom-rh; } if(ry-rh<edge.top) { rect.v.y *= -1; rect.y = edge.top+rh; } } function ifCircleOnEdgeBounce(circle) { let cx = circle.x; let cy = circle.y; let cr = circle.r; if(cx+cr>edge.right) { circle.v.x *= -1; circle.x = edge.right-cr; } if(cx-cr<edge.left) { circle.v.x *= -1; circle.x = edge.left+cr; } if(cy+cr>edge.bottom) { circle.v.y *= -1; circle.y = edge.bottom-cr; } if(cy-cr<edge.top) { circle.v.y *= -1; circle.y = edge.top+cr; } } // create globals var canvases={cvs:null,ctx:null,buffer1cvs:null,buffer1ctx:null,buffer2cvs:null,buffer2ctx:null}, // visable and hidden canvases cw, // canvas width ch, // canvas height camera={zoom:1,angle:0,x:0,y:0}, // affects how everything is drawn updateFPS=60, gameStarted=false, drawMode=0, // 0=normal, 1=zoomed, 2=zoomed/rotated, set automatically depending on camera absDraw=false, curCtx, // what canvas to draw to maxCvsSize, // used by second buffer canvasScale=1, difx=0, // offsets for drawing dify=0, seperateInputLoop=true, edge={top:null,bottom:null,left:null,right:null}, // used by if___OnEdgeBounce, set to canvas size at setup, can be changed whenever drawLimitLeft, drawLimitRight, drawLimitTop, drawLimitBottom, sizeDif, bug="\uD83D\uDC1B", loadingCircle, loadAng=0, optionsHover=0, pauseHover=0, optionsMenu=false, optionsButtons={}, clickSound, paused=false, screenSize="1:1", autoScale=1, images=[], // put image paths here imagePaths=[], imgs=[], sprites={}, // loaded images audio=[], // put audio paths here audioPaths=[], sounds={}, // loaded sounds abuffer = [], // audio nodes shoved here volumeList = [], // gain nodes shoved here audioLoadedLength=0, volume={sfx:1,bgm:1}; /* options future: modefileable checklist key bindings */ cursor = {sprite:null,alignment:1,show:true}, // 0=topleft, 1=centered mouseOnCanvas=false; const acceptableChars="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890_-. ";//for image names const AudioContext = window.AudioContext||window.webkitAudioContext; var context; var sfxVolumeNode; var bmgVolumeNode; document.getElementById("game").onmouseout = function() {mouseOnCanvas = false;} document.getElementById("game").onmouseover = function() {mouseOnCanvas = true;} //setup canvases and input function setup(physicsFPS) { updateFPS = physicsFPS; canvases.cvs = document.getElementById("game"); canvases.ctx = canvases.cvs.getContext("2d", { alpha: false }); canvases.cvs.onmousedown = function () {if(!gameStarted){loadImagesAndSounds();gameStarted=true;}} createCanvas("buffer1"); createCanvas("buffer2"); canvases.ctx.imageSmoothingEnabled = false; canvases.buffer1ctx.imageSmoothingEnabled = false; canvases.buffer2ctx.imageSmoothingEnabled = false; maxCvsSize=Math.max(canvases.cvs.width,canvases.cvs.height); sizeDif=maxCvsSize-Math.min(canvases.cvs.width,canvases.cvs.height); cw=canvases.cvs.width; ch=canvases.cvs.height; edge={top:0,bottom:ch,left:0,right:cw}; addFont(); addStyle(); addListenersTo(canvases.cvs); curCtx = canvases.ctx; requestAnimationFrame(startButton); function startButton() { curCtx.fillStyle="#2d2d2d"; curCtx.fillRect(0,0,cw,ch);//debugger; circle(cw/2,ch/2,27,"#066312"); circle(cw/2,ch/2,23,"#149124"); shape(cw/2,ch/2,[{x:-7,y:-15},{x:-7,y:15},{x:15,y:0}],"#47f55d"); if(!gameStarted) {requestAnimationFrame(startButton);} } } function drawLoop() { cw=canvases.cvs.width; ch=canvases.cvs.height; scaleCanvases(); switchDrawMode(); resizeBuffers(); clearCanvases(); var limitModifyer = 0; if(drawMode==2) {limitModifyer=canvases.buffer2cvs.width-maxCvsSize;} drawLimitLeft = -camera.x - (drawMode==2?sizeDif:0) - limitModifyer; drawLimitRight = -camera.x + maxCvsSize + (drawMode==2?sizeDif:0) + limitModifyer; drawLimitTop = -camera.y -(drawMode==2?sizeDif:0) - limitModifyer; drawLimitBottom = -camera.y + maxCvsSize + (drawMode==2?sizeDif:0) + limitModifyer; draw(); render(); curCtx=canvases.ctx; difx=0;dify=0; var camCache = {x:camera.x,y:camera.y}; var drawModeCache = drawMode; camera.x=0;camera.y=0; drawMode=0; absDraw=true; absoluteDraw(); absDraw=false; drawButtons(); drawOptionsMenu(); drawCursor(); drawMode=drawModeCache; camera.x = camCache.x; camera.y = camCache.y; requestAnimationFrame(drawLoop); } function updateLoop() { if(seperateInputLoop==false) { handleOptionsInput(); } sfxVolumeNode.gain.value = volume.sfx; bmgVolumeNode.gain.value = volume.bgm; if(!paused) { update(); } if(seperateInputLoop==false) { resetInput(); } } function inputLoop() { handleOptionsInput(); if(!paused) { input(); } resetInput(); }