var canvas; var currentSound = new Audio(); const GOODQUOTES=[ "Let the conversion begin...", "All the bots, come to ME", "Let's convert them ALL", "What a fantastic machine you have built", "I like it!",] const BADQUOTES=[ "I smell a funny one here...", "I have found a flaw!", "How could you build me so disfunctionally?", "???", "You seemed to miss a step...",] var TITLESCREENTIMELINE=new Date().getTime() const TITLETIMELINE = [ {time:0,cb: (ctx)=>{ SCENE_DRAW=(ctx)=>{ ctx.globalAlpha=Math.min((new Date().getTime()-TITLESCREENTIMELINE)/3000,1) ctx.drawImage(IMAGE_SIG,canvas.width/2-243,canvas.height/2-22) } } }, {time:4000,cb: (ctx)=>{ SCENE_DRAW=(ctx)=>{ ctx.globalAlpha=Math.max( (7000-(new Date().getTime()-TITLESCREENTIMELINE))/3000 ,0) ctx.drawImage(IMAGE_SIG,canvas.width/2-243,canvas.height/2-22) } } }, {time:4000,cb: (ctx)=>{ currentSound.src="Super 8 Old Movie Projector - Gaming Sound Effect.mp3" currentSound.play() } }, {time:7000,cb: (ctx)=>{ SCENE_DRAW=(ctx)=>{ SCENEALPHA=0 if (Math.random()<=0.02) { ctx.globalAlpha=0.5 ctx.drawImage(IMAGE_TITLE,canvas.width/2+Math.random()*640260,canvas.height/2+Math.random()*64-70) } else { ctx.globalAlpha=0.75+Math.random()*0.25 ctx.drawImage(IMAGE_TITLE,canvas.width/2-260,canvas.height/2-70) } } } }, {time:15000,cb: (ctx)=>{ currentSound.src="Shostakovich_ Symphony No. 9.mp3" currentSound.loop=true currentSound.play() SCENE_DRAW=(ctx)=>{ ctx.globalAlpha=0.9 ctx.drawImage(IMAGE_TITLE,canvas.width/2-260,canvas.height/2-70) ctx.font="bold 48px 'Zilla Slab', serif" ctx.fillStyle="black" ctx.strokeStyle="white" ctx.textAlign = "center" ctx.fillText("- Click to Play -",canvas.width/2,canvas.height*0.9) ctx.strokeText("- Click to Play -",canvas.width/2,canvas.height*0.9) } } }, ] var SCENEBACKGROUND = "black" var SCENEALPHA = 1.0 var CURRENTTIMELINE = [] var SCENE_DRAW = ()=>{} const WAITING = 0; const RUNNING = 1; const REVIEWING = 2; const TESTING = 3; const FINISH = 4; const PAUSED = 5; const MAINMENU = 6; const TITLE = 7; const STARTUP = 8; const INFO = 9; const NORMAL_TEST = 0; const EVEN_LENGTH_TEST = 1; //Only generate even length tapes. var ISTESTING = false; var MOUSEOVERTIME = -1 const UP = 0; const RIGHT = 1; const DOWN = 2; const LEFT = 3; const RED = "R"; const BLUE = "B"; const GREEN = "G"; const YELLOW = "Y"; const PURPLE = "P"; const PINK = "p"; const BLACK = "b"; const GRAY = "g"; const ALIVE = 0; const DEAD = 1; const DONE = 2; var BOT_X = -1 var BOT_Y = -1 var BOT_DIR = RIGHT var BOT_STATE = ALIVE var BOT_TAPE = "RB" var BOT_START_TAPE = BOT_TAPE var BOT_QUEUE = [] var DELETEMODE = false var DRAGGING = false var MOUSEDOWN = false var LAST_MOUSE_X=0; var LAST_MOUSE_Y=0; var DRAG_X = -1 var DRAG_Y = -1 var BOT_DID_NOT_REACH_EXIT = false var TOOLTIPDISPLAYED = undefined var MOVEMODE = false var STARTDRAG = undefined var TEST_RANDOM_TAPE = "" var MESSAGETIMER = -1 var EXPECTED = true //True means the bot was supposed to accepted, false means the bot was supposed to be rejected. var RESULT = true //True means you won. False means you lost. var TESTSTEPS = 0 //How long it takes the tests to run. var BOT_PREVX = BOT_X var BOT_PREVY = BOT_Y var LASTPOSITIONUPDATE = 0 var BRIDGEDBELT = false var MOBILE = false var BELTDOWN = {type:"BELT",direction:DOWN/*,direction2 - defines a secondary direction. For two belts at once.*/} var BELTRIGHT = {type:"BELT",direction:RIGHT} var BELTUP = {type:"BELT",direction:UP} var BELTLEFT = {type:"BELT",direction:LEFT} var BRANCHDOWN = {type:"BRANCH",direction:DOWN,color1:RED,color2:BLUE} //color 1 points clockwise(right), color 2 points counter-clockwise(left) var BRANCHLEFT = {type:"BRANCH",direction:LEFT,color1:RED,color2:BLUE} var BRANCHRIGHT = {type:"BRANCH",direction:RIGHT,color1:RED,color2:BLUE} var BRANCHUP = {type:"BRANCH",direction:UP,color1:RED,color2:BLUE} var WRITERDOWN = {type:"WRITER",direction:DOWN,color1:RED/*overwrite - if turned on, the writer overwrites the current tape position instead of appending.*/} var WRITERLEFT = {type:"WRITER",direction:LEFT,color1:RED} var WRITERRIGHT = {type:"WRITER",direction:RIGHT,color1:RED} var WRITERUP = {type:"WRITER",direction:UP,color1:RED} var DEF_BRANCHUP_RB = {img:ID_BRANCH,color1:RED,color2:BLUE,type:"BRANCH"} var DEF_BRANCHUP_BR = {img:ID_BRANCH,color1:BLUE,color2:RED,type:"BRANCH"} var DEF_BRANCHUP_GY = {img:ID_BRANCH,color1:GREEN,color2:YELLOW,type:"BRANCH"} var DEF_BRANCHUP_YG = {img:ID_BRANCH,color1:YELLOW,color2:GREEN,type:"BRANCH"} var DEF_BRANCHUP_PPI = {img:ID_BRANCH,color1:PURPLE,color2:PINK,type:"BRANCH"} var DEF_BRANCHUP_PIP = {img:ID_BRANCH,color1:PINK,color2:PURPLE,type:"BRANCH"} var DEF_BRANCHUP_BLGR = {img:ID_BRANCH,color1:BLACK,color2:GRAY,type:"BRANCH"} var DEF_BRANCHUP_GRBL = {img:ID_BRANCH,color1:GRAY,color2:BLACK,type:"BRANCH"} var DEF_WRITERRIGHT_R = {img:ID_WRITER,color1:RED,type:"WRITER"} var DEF_WRITERRIGHT_B = {img:ID_WRITER,color1:BLUE,type:"WRITER"} var DEF_WRITERRIGHT_G = {img:ID_WRITER,color1:GREEN,type:"WRITER"} var DEF_WRITERRIGHT_Y = {img:ID_WRITER,color1:YELLOW,type:"WRITER"} var DEF_WRITERRIGHT_P = {img:ID_WRITER,color1:PURPLE,type:"WRITER"} var DEF_WRITERRIGHT_PI = {img:ID_WRITER,color1:PINK,type:"WRITER"} var DEF_WRITERRIGHT_BL = {img:ID_WRITER,color1:BLACK,type:"WRITER"} var DEF_WRITERRIGHT_GR = {img:ID_WRITER,color1:GRAY,type:"WRITER"} var DEF_CONVEYOR = {img:ID_CONVEYOR,type:"BELT"} var GRID_W = 32 var GRID_H = 32 var GRID_X = 20 var GRID_Y = 20 var ITEM_DIRECTION=RIGHT; var dashOffset=0 var SUBMENU = { visible:false, width:0, height:0, buttons:[] } var BUTTON_SELECTED = undefined var ITEM_SELECTED = undefined var KEY_ROTATION_RIGHT = ["D","L","d","l","6","ArrowRight"] var KEY_ROTATION_LEFT = ["A","G","a","h","4","ArrowLeft"] var KEY_ROTATION_UP = ["W","K","w","k","8","ArrowUp"] var KEY_ROTATION_DOWN = ["S","J","s","j","2","ArrowDown"] var KEY_BRIDGED_BELT = ["Shift"] var CONVEYOR_BUILD_BUTTON = {img:ID_CONVEYOR,x:-1,y:-1,w:-1,h:-1,lastselected:DEF_CONVEYOR,tooltip:"Conveyor Belt\nMoves bots in a direction.\n\nHold (Shift) to bridge over other belts",mobileTooltip:"Conveyor Belt\nMoves bots in a direction.\n\nHold button down to toggle bridge mode.\nUsed to bridge over other belts"} var BRANCH_BUILD_BUTTON = {img:ID_BRANCH,x:-1,y:-1,w:-1,h:-1,submenu_buttons:[DEF_BRANCHUP_RB,DEF_BRANCHUP_BR,DEF_BRANCHUP_GY,DEF_BRANCHUP_YG,DEF_BRANCHUP_PPI,DEF_BRANCHUP_PIP,DEF_BRANCHUP_BLGR,DEF_BRANCHUP_GRBL],lastselected:undefined,default:DEF_BRANCHUP_RB,tooltip:"Branch\nReads next tape and moves bot in the\nmatching color direction.\n\nMoves bot forward and does not consume\ntape if no match found."} var WRITER_BUILD_BUTTON = {img:ID_WRITER,x:-1,y:-1,w:-1,h:-1,submenu_buttons:[DEF_WRITERRIGHT_R,DEF_WRITERRIGHT_B,DEF_WRITERRIGHT_G,DEF_WRITERRIGHT_Y,DEF_WRITERRIGHT_P,DEF_WRITERRIGHT_PI,DEF_WRITERRIGHT_BL,DEF_WRITERRIGHT_GR],lastselected:undefined,default:DEF_WRITERRIGHT_R,tooltip:"Writer\nWrites a color to the end of the tape."} var ROTATE_CLOCKWISE_BUTTON = {img:ID_ROTATE_CLOCKWISE,x:-1,y:-1,w:-1,h:-1,cb:rotateClockwise,tooltip:"Rotate selection clockwise." } var ROTATE_COUNTERCLOCKWISE_BUTTON = {img:ID_ROTATE_COUNTERCLOCKWISE,x:-1,y:-1,w:-1,h:-1,cb:rotateCounterClockwise,tooltip:"Rotate selection counter-clockwise." } var PLAY_BUTTON = {img:ID_PLAY,x:-1,y:-1,w:-1,h:-1,cb:runGameSimulation,tooltip:"Test your great machine.\n\nConvert all the bots!" } var PAUSE_BUTTON = {img:ID_PAUSE,x:-1,y:-1,w:-1,h:-1,cb:pauseGameSimulation,tooltip:"Pause the great machine." } var RESET_BUTTON = {img:ID_RESET,x:-1,y:-1,w:-1,h:-1,cb:resetSimulation,tooltip:"Reset the great machine." } var DELETE_BUTTON = {img:ID_DELETE,x:-1,y:-1,w:-1,h:-1,cb:toggleDeleteMode,tooltip:"Remove a piece on the field." } var HOME_BUTTON = {img:ID_HOME,x:-1,y:-1,w:-1,h:-1,cb:goHome,tooltip:"Go back to the level selection menu.\n\nYour progress will be saved automatically." } var MOVE_BUTTON = {img:ID_MOVE_BUTTON,x:2,y:2,w:32,h:32,cb:toggleMoveMode,tooltip:"Scroll the view area." } var AUDIO_BUTTON = { bounds:{x:4,y:4,w:40,h:40}, cb:()=>{ currentSound.muted=!currentSound.muted } } var INFO_BUTTON = { bounds:{x:4,y:44,w:40,h:40}, cb:()=>{ gameState=INFO } } var INFO_HOME_BUTTON = { bounds:{x:4,y:4,w:40,h:40}, cb:()=>{ goHome() } } var MENU = { visible:false, buttons:[CONVEYOR_BUILD_BUTTON,BRANCH_BUILD_BUTTON,WRITER_BUILD_BUTTON,ROTATE_COUNTERCLOCKWISE_BUTTON,ROTATE_CLOCKWISE_BUTTON,DELETE_BUTTON,PLAY_BUTTON,RESET_BUTTON,HOME_BUTTON] } function toggleMoveMode() { setMoveMode(!MOVEMODE) } function saveLevelData() { completedStages[gameStage.name].data=deepCopy(gameGrid) localStorage.setItem("game",JSON.stringify(completedStages)) } function goHome() { saveLevelData() MENU.visible=false BRIDGEDBELT=false setMoveMode(false) GRID_X=20 GRID_Y=20 ITEM_SELECTED=undefined for (var button of MENU.buttons) { if (button.submenu_buttons) { button.lastselected=button.default } } gameGrid=[] gameState=MAINMENU } function runGameSimulation(){ if (gameState!==PAUSED) { gameState=TESTING ISTESTING=true BOT_PREVX=-100 BOT_PREVY=-100 BOT_TAPE="" BOT_START_TAPE=undefined setMoveMode(false) generateBotQueue() //console.log(BOT_QUEUE) setTimeout(()=>{ ISTESTING=false if (BOT_QUEUE.length>0) { BOT_TAPE=BOT_QUEUE[0] } else { BOT_TAPE=TEST_RANDOM_TAPE EXPECTED=gameStage.accept(BOT_TAPE) } BOT_START_TAPE=BOT_TAPE BOT_X=gameStage.start.x BOT_Y=gameStage.start.y BOT_PREVX=BOT_X BOT_PREVY=BOT_Y BOT_STATE=ALIVE gameState=WAITING BOT_DIR=RIGHT gameState=RUNNING if (gameSpeed===-1) { gameSpeed=1000/1 } gameState=RUNNING MESSAGETIMER=new Date().getTime()+3000 for (var i=0;itrue} var STAGE2 = { name:"Blue Blue", objective:"Accept only Blue Bots", level:createGrid(5,5,4,2), start:{x:0,y:2}, accept:(tape)=>{ if (tape.length===0) { return false; } for (var i=0;i{ var reds=0; var blues=0; for (var i=0;i{ return true; } } var TUTORIAL2 = { name:"Branches", objective:"We have to make sure we are sending robots that meet our needs! Use the branch to filter out robots that start with a red signal. Send only those to the exit!", level:createGrid(5,5,4,2), start:{x:0,y:2}, locked:[WRITER_BUILD_BUTTON], tutorial:true, accept:(tape)=>{ if (tape[0]===RED) { return true; } else { return false; } } } var TUTORIAL3 = { name:"Writers", objective:"CONVERSION! It's time to convert robots to what they should be. Add 3 blue signals to every bot that comes through. We shall convert them all!", level:createGrid(5,5,4,2), start:{x:0,y:2}, tutorial:true, accept:(tape)=>{ return tape+BLUE+BLUE+BLUE } } var TUTORIAL4 = { name:"More Colors", objective:"You may be required to use different colors, either for your purposes or mine. For this robot cycle, convert all blue and red signals to yellow signals.", level:createGrid(5,5,4,2), start:{x:0,y:2}, accept:(tape)=>{ var newTape = "" for (var i=0;i=3) { break; } } if (RESULT) { completedStages[gameStage.name].complete=true completedStages[gameStage.name].score=TESTSTEPS } //console.log("Test Steps: "+TESTSTEPS) } } function getSimulatedBotResult(tape) { var simulatedBoard = deepCopy(gameGrid) resetBot(gameStage.start.x,gameStage.start.y,TESTING,tape) const MAX_ITERATIONS=2000 var iterations=0 while (iterations0) { var mask = remainingVal&1 if (mask===1) { tape="B"+tape } else { tape="R"+tape } remainingVal=remainingVal>>>1 } return tape; } function setNextSquare(offsetX,offsetY) { if (gameGrid[BOT_Y+offsetY]!==undefined) { var nextSquare = gameGrid[BOT_Y+offsetY][BOT_X+offsetX]; if (!ISTESTING) { BOT_PREVX=BOT_X BOT_PREVY=BOT_Y } LASTPOSITIONUPDATE=new Date().getTime() BOT_X+=offsetX BOT_Y+=offsetY return nextSquare } else { gameState = REVIEWING BOT_STATE = DEAD endARound() return undefined } } function runBot(testing) { //console.log(new Date().getTime()) if ((lastGameUpdate=button.x && getMousePos(e).x<=button.x+button.w && getMousePos(e).y>=button.y && getMousePos(e).y<=button.y+button.h) } function setMoveMode(mode) { MOVEMODE=mode if (MOVEMODE) { document.body.style.cursor="move" } else { if (DELETEMODE) { document.body.style.cursor="url('delete_cursor.png') 8 8,auto" } else { document.body.style.cursor="url('cursor.png') 8 8,auto" } } } function clickEvent(e) { //console.log(MENU.buttons) if (e instanceof TouchEvent) { MOBILE=true e.preventDefault() } else { MOBILE=false } var mousepos = getMousePos(e) LAST_MOUSE_X=mousepos.x LAST_MOUSE_Y=mousepos.y MOUSEOVERTIME=-1 MOUSEDOWN=true if (gameState===STARTUP) { currentSound.play() setupTitleScreen() } if (gameState===TITLE) { if (new Date().getTime()-TITLESCREENTIMELINE>=15000) { gameState=MAINMENU } } if (gameState===MAINMENU) { if (MouseOverBounds(AUDIO_BUTTON.bounds)) {AUDIO_BUTTON.cb();return;} if (MouseOverBounds(INFO_BUTTON.bounds)) {INFO_BUTTON.cb();return;} } if (gameState===INFO) { if (MouseOverBounds(INFO_HOME_BUTTON.bounds)) {INFO_HOME_BUTTON.cb();return;} } if (MENU.visible) { for (var button of MENU.buttons) { if (ButtonIsUnlocked(button)) { if (mouseOverButton(canvas,e,button)) { if (MOBILE&&button===CONVEYOR_BUILD_BUTTON) { setTimeout(()=>{ if (MOUSEDOWN&& LAST_MOUSE_X>=CONVEYOR_BUILD_BUTTON.x&& LAST_MOUSE_X<=CONVEYOR_BUILD_BUTTON.x+CONVEYOR_BUILD_BUTTON.w&& LAST_MOUSE_Y>=CONVEYOR_BUILD_BUTTON.y&& LAST_MOUSE_Y<=CONVEYOR_BUILD_BUTTON.y+CONVEYOR_BUILD_BUTTON.h) { BRIDGEDBELT=!BRIDGEDBELT } },500) } if (button.cb!==undefined) { button.cb() return; } else { DELETEMODE=false document.body.style.cursor="url('cursor.png') 8 8,auto" if (button.submenu_buttons&&button.submenu_buttons.length>0) { BUTTON_SELECTED=button; SUBMENU.visible=true; SUBMENU.buttons=[] var index = 0; for (var button2 of BUTTON_SELECTED.submenu_buttons) { var buttonX = ((index%3)*48)+16 var buttonY = canvas.height*0.8-(Math.floor(index/3)*48)-40 SUBMENU.buttons.push({def:button2,img:button2.img,x:buttonX,y:buttonY,w:32,h:32}) index++; } } if (ITEM_SELECTED===button.lastselected&&!MOVEMODE) { if (!MOBILE) { ITEM_SELECTED=undefined } } else { ITEM_SELECTED=button.lastselected } //console.log(button) setMoveMode(false) return } } } } if (mouseOverButton(canvas,e,MOVE_BUTTON)&&!DELETEMODE) { MOVE_BUTTON.cb() return } } if (gridModeIsAvailable()&&ITEM_SELECTED===undefined) { setMoveMode(true) } if (!MOBILE&&e.button!==0) { e.preventDefault() if (gridModeIsAvailable()) { setMoveMode(true) } else { setMoveMode(false) } } if (MOVEMODE) { //Cannot handle building until out of move mode. STARTDRAG=getMousePos(e) return; } //console.log(getGridCoords(getMousePos(e))) if ((ITEM_SELECTED!==undefined||DELETEMODE)&&!MOVEMODE) { var clickCoords = getGridCoords(getMousePos(e)) if (coordsInBounds(clickCoords)) { modifyBoard(clickCoords,ITEM_SELECTED) DRAGGING = true DRAG_X = clickCoords.x DRAG_Y = clickCoords.y //console.log(gameGrid) } } } function coordsInBounds(coords) { return coords.x>=0&&coords.y>=0&&coords.y0) { var currentEvent = CURRENTTIMELINE[0] if (currentEvent.time=x&&LAST_MOUSE_X<=x+w && LAST_MOUSE_Y>=y-6&&LAST_MOUSE_Y<=y+6) { var mouseCursorPos = Math.round((LAST_MOUSE_X-x)/8)*8 switch (mouseCursorPos) { case 0:{ gameSpeed=-1; }break; case 8:{ gameSpeed=1000/1; }break; case 16:{ gameSpeed=1000/2; }break; case 24:{ gameSpeed=1000/3; }break; case 32:{ gameSpeed=1000/4; }break; case 40:{ gameSpeed=1000/6; }break; case 48:{ gameSpeed=1000/8; }break; case 56:{ gameSpeed=1000/16; }break; case 64:{ gameSpeed=1000/32; }break; } } ctx.beginPath() ctx.moveTo(x+cursorX,y-4) ctx.lineTo(x+cursorX,y+4) ctx.stroke(); } function DrawWrapText(text,x,y,w,fontHeight,ctx,topToBottom=false/*Set to true to draw from the position downward. Defaults to drawing upwards.*/) { var arr = text.split(" ") var finalText = [] for (var i=0;iw) { finalText.push(arr[i]) } else { if (finalText.length===0) { finalText.push(arr[i]) } else { finalText[finalText.length-1]+=" "+arr[i] } } } for (var i=0;i5*5) { ySpacingMult=(ySpacing*5)/(Math.ceil(tape.length/5)*ySpacing) } for (var i=0;iwidth-24) { xOffset=0; yOffset+=ySpacing*ySpacingMult; } } } function createVerticalGradient(x,y,up,ctx,scale) { var gradient = ctx.createLinearGradient(x, y+32*scale*((up)?1:0), x, y+32*scale*((up)?0:1)); gradient.addColorStop(0,"rgb(124,162,157)") gradient.addColorStop(0.31,"black") gradient.addColorStop(0.6,"rgb(124,162,157)") gradient.addColorStop(0.61,"black") gradient.addColorStop(0.9,"rgb(124,162,157)") gradient.addColorStop(0.91,"white") gradient.addColorStop(1,"white") return gradient } function createHorizontalGradient(x,y,right,ctx,scale) { var gradient = ctx.createLinearGradient(x+32*scale*((right)?0:1), y, x+32*scale*((right)?1:0), y); gradient.addColorStop(0,"rgb(124,162,157)") gradient.addColorStop(0.31,"black") gradient.addColorStop(0.6,"rgb(124,162,157)") gradient.addColorStop(0.61,"black") gradient.addColorStop(0.9,"rgb(124,162,157)") gradient.addColorStop(0.91,"white") gradient.addColorStop(1,"white") return gradient } function DrawSingleConveyor(x,y,dir,ctx,scale,ghost=false) { ctx.lineWidth = 16*scale; ctx.lineCap = "square" if (ghost) { ctx.globalAlpha=0.6 ctx.strokeStyle="rgb(0,255,0)" } else { ctx.globalAlpha=1 ctx.strokeStyle="rgb(124,162,157)" } ctx.setLineDash([]) if (dir===LEFT||dir===RIGHT) { ctx.beginPath() ctx.moveTo(x+8*scale,y+16*scale) ctx.lineTo(x+24*scale,y+16*scale) ctx.stroke() ctx.setLineDash([5*scale,5*scale]) ctx.lineDashOffset = -dashOffset*((dir===LEFT)?-1*scale:1*scale); ctx.strokeStyle=createHorizontalGradient(x,y,dir===RIGHT,ctx,scale) ctx.lineWidth = 3.5*scale; ctx.beginPath() ctx.moveTo(x+2*scale,y+16*scale) ctx.lineTo(x+30*scale,y+16*scale) ctx.stroke() } else { ctx.beginPath() ctx.moveTo(x+16*scale,y+8*scale) ctx.lineTo(x+16*scale,y+24*scale) ctx.stroke() ctx.strokeStyle="rgb(34,62,57)" ctx.setLineDash([5*scale,5*scale]) ctx.lineDashOffset = -dashOffset*((dir===DOWN)?1:-1); ctx.strokeStyle=createVerticalGradient(x,y,dir===UP,ctx,scale) ctx.lineWidth = 3.5*scale; ctx.beginPath() ctx.moveTo(x+16*scale,y+2*scale) ctx.lineTo(x+16*scale,y+30*scale) ctx.stroke() } ctx.globalAlpha=1 } function GetOppositeDirection(dir) { switch (dir) { case UP:{return DOWN} case DOWN:{return UP} case LEFT:{return RIGHT} case RIGHT:{return LEFT} } } function DrawConnectedConveyor(x,y,ctx,connections,dir,scale,pass=0) { ctx.lineWidth = 15*scale; ctx.lineCap = "round" ctx.strokeStyle="rgb(124,162,157)" switch (Object.keys(connections).length) { case 0:{ DrawSingleConveyor(x*scale,y*scale,dir,ctx,scale) }break; default:{ ctx.lineWidth = 15*scale; ctx.lineCap = "square" ctx.strokeStyle="rgb(124,162,157)" ctx.setLineDash([]) ctx.beginPath() ctx.moveTo(x+16*scale,y+16*scale) var endingPoint={x:0,y:0} switch (dir) { case UP:{startingPoint={x:0,y:-12*scale};endingPoint={x:0,y:-14*scale}}break; case DOWN:{startingPoint={x:0,y:12*scale};endingPoint={x:0,y:14*scale}}break; case RIGHT:{startingPoint={x:12*scale,y:0};endingPoint={x:14*scale,y:0}}break; case LEFT:{startingPoint={x:-12*scale,y:0};endingPoint={x:-14*scale,y:0}}break; } ctx.moveTo(x+16*scale+startingPoint.x,y+16*scale+startingPoint.y) ctx.lineTo(x+16*scale+endingPoint.x,y+16*scale+endingPoint.y) if (pass===0) {ctx.stroke();} for (var connection of Object.keys(connections)) { var startingPoint={x:x,y:y} switch (Number(connection)) { case UP:{startingPoint={x:x+16*scale,y:y+8*scale}}break; case DOWN:{startingPoint={x:x+16*scale,y:y+24*scale}}break; case RIGHT:{startingPoint={x:x+24*scale,y:y+16*scale}}break; case LEFT:{startingPoint={x:x+8*scale,y:y+16*scale}}break; } ctx.lineWidth = 16*scale; ctx.lineCap = "square" ctx.strokeStyle="rgb(124,162,157)" ctx.setLineDash([]) ctx.beginPath() ctx.moveTo(startingPoint.x,startingPoint.y) ctx.lineTo(x+16*scale,y+16*scale) if (pass===0) {ctx.stroke()} ctx.setLineDash([5*scale,5*scale]) ctx.lineDashOffset = -dashOffset*1; if (Number(connection)===RIGHT||Number(connection)===LEFT) { ctx.strokeStyle=createHorizontalGradient(x,y,Number(connection)===LEFT,ctx,scale) } else { ctx.strokeStyle=createVerticalGradient(x,y,Number(connection)===UP,ctx,scale) } ctx.lineWidth = 3.5*scale; ctx.beginPath() startingPoint={x:x,y:y} switch (Number(connection)) { case UP:{startingPoint={x:x+16*scale,y:y+2*scale}}break; case DOWN:{startingPoint={x:x+16*scale,y:y+30*scale}}break; case RIGHT:{startingPoint={x:x+30*scale,y:y+16*scale}}break; case LEFT:{startingPoint={x:x+2*scale,y:y+16*scale}}break; } ctx.moveTo(startingPoint.x,startingPoint.y) ctx.lineTo(x+16*scale,y+16*scale) if (pass===1) {ctx.stroke()} } if (dir===RIGHT||dir===LEFT) { ctx.strokeStyle=createHorizontalGradient(x,y,dir===LEFT,ctx,scale) } else { ctx.strokeStyle=createVerticalGradient(x,y,dir===UP,ctx,scale) } ctx.setLineDash([5*scale,5*scale]) ctx.lineWidth = 3.5*scale; ctx.beginPath() switch (dir) { case UP:{startingPoint={x:0,y:-14*scale}}break; case DOWN:{startingPoint={x:0,y:14*scale}}break; case RIGHT:{startingPoint={x:14*scale,y:0}}break; case LEFT:{startingPoint={x:-14*scale,y:0}}break; } ctx.moveTo(x+16*scale,y+16*scale) ctx.lineTo(x+16*scale+startingPoint.x,y+16*scale+startingPoint.y) if (pass===1) {ctx.stroke();} }break; } } function HasRelativeConnection(x,y,dir) { return (gameGrid[y][x].direction===dir || (gameGrid[y][x].direction2!==undefined&&gameGrid[y][x].direction2===dir) || ( gameGrid[y][x].type==="BRANCH" && (gameGrid[y][x].direction===getClockwiseDirection(dir) || gameGrid[y][x].direction===getCounterClockwiseDirection(dir) || (gameGrid[y][x].direction2!==undefined&&gameGrid[y][x].direction2===getClockwiseDirection(dir)) || (gameGrid[y][x].direction2!==undefined&&gameGrid[y][x].direction2===getCounterClockwiseDirection(dir))) ) || ( x===gameStage.start.x&&y===gameStage.start.y ) ) } function RenderConveyor(x,y,ctx,icon_definition,dir=0,background=undefined,grid=undefined,scale) { if (grid===undefined) { //This is the button version. if (BRIDGEDBELT) { DrawSingleConveyor(x,y,dir+1,ctx,scale,true) DrawSingleConveyor(x,y,dir,ctx,scale) } else { DrawSingleConveyor(x,y,dir,ctx,scale) } } else if (gameGrid[grid.y][grid.x].direction2!==undefined) { DrawSingleConveyor(x,y,dir,ctx,scale) DrawSingleConveyor(x,y,gameGrid[grid.y][grid.x].direction2,ctx,scale) } else { var connections = {} if (grid.x>0) {if (HasRelativeConnection(grid.x-1,grid.y+0,RIGHT)){connections[LEFT]=true}} if (grid.x0) {if (HasRelativeConnection(grid.x+0,grid.y-1,DOWN)){connections[UP]=true}} if (grid.y=bounds.x&& LAST_MOUSE_X<=bounds.x+bounds.w&& LAST_MOUSE_Y>=bounds.y&& LAST_MOUSE_Y<=bounds.y+bounds.h) } function GetArrowImage(col) { switch (col) { case RED:{ return ID_ARROW_R }break; case BLUE:{ return ID_ARROW_B }break; case GREEN:{ return ID_ARROW_G }break; case YELLOW:{ return ID_ARROW_Y }break; case PURPLE:{ return ID_ARROW_P }break; case PINK:{ return ID_ARROW_PI }break; case BLACK:{ return ID_ARROW_BL }break; case GRAY:{ return ID_ARROW_GR }break; } } function GetDotImage(col) { switch (col) { case RED:{ return ID_DOT_R }break; case BLUE:{ return ID_DOT_B }break; case GREEN:{ return ID_DOT_G }break; case YELLOW:{ return ID_DOT_Y }break; case PURPLE:{ return ID_DOT_P }break; case PINK:{ return ID_DOT_PI }break; case BLACK:{ return ID_DOT_BL }break; case GRAY:{ return ID_DOT_GR }break; } } function RenderSubmenu(ctx) { if (SUBMENU.visible) { ctx.fillStyle="#76abab" var submenuRows = BUTTON_SELECTED.submenu_buttons.length/3 var submenuCols = 3 ctx.fillRect(0,canvas.height*0.8-submenuRows*48-16,submenuCols*48+16,submenuRows*48+16) var index = 0; for (var button of BUTTON_SELECTED.submenu_buttons) { if (SubmenuButtonIsUnlocked(button)) { var buttonX = ((index%3)*48)+16 var buttonY = canvas.height*0.8-(Math.floor(index/3)*48)-40 RenderIcon(buttonX,buttonY,ctx,button,ITEM_DIRECTION,(LAST_MOUSE_X>=buttonX&&LAST_MOUSE_X<=buttonX+32&&LAST_MOUSE_Y>=buttonY&&LAST_MOUSE_Y<=buttonY+32)?"#555555":"#b5b5b5") index++; } } } } function SubmenuButtonIsUnlocked(button) { return gameStage.tutorial===undefined||!gameStage.tutorial||button.color1===RED||button.color1===BLUE } function ButtonIsUnlocked(button) { if (button===HOME_BUTTON&&gameStage.tutorial&&!LevelIsBeat(gameStage.name)) { return false } return gameStage.locked===undefined||(!gameStage.locked.includes(button)) } function isMouseOverButton(button) { return LAST_MOUSE_X>=button.x&& LAST_MOUSE_X<=button.x+button.w&& LAST_MOUSE_Y>=button.y&& LAST_MOUSE_Y<=button.y+button.h } function DisplayTooltip(button,ctx) { //240x(20*lines) (16pt font) var tooltipSplit=[] if (MOBILE&&button.mobileTooltip!==undefined){ tooltipSplit=button.mobileTooltip.split("\n") } else { tooltipSplit=button.tooltip.split("\n") } var tooltipBounds = { x:Math.min(Math.max(LAST_MOUSE_X-120,0),canvas.width*0.75-240), y:LAST_MOUSE_Y-tooltipSplit.length*20-4, w:240, h:tooltipSplit.length*16+8 } ctx.globalAlpha=0.9 ctx.fillStyle="#20424a" ctx.fillRect(tooltipBounds.x,tooltipBounds.y,tooltipBounds.w,tooltipBounds.h) ctx.strokeStyle="white" ctx.lineWidth=1 ctx.setLineDash([]) ctx.strokeRect(tooltipBounds.x,tooltipBounds.y,tooltipBounds.w,tooltipBounds.h) ctx.fillStyle="white" ctx.textAlign = "left" for (var i=0;i=4000 || !MOBILE&&new Date().getTime()-MOUSEOVERTIME>=1000) { mouseOverButton = button } } if (button.lastselected) { RenderIcon(buttonX,buttonY,ctx,button.lastselected,(button.cb===undefined)?ITEM_DIRECTION:0,"#b5b5b5") } else { AddButton(button.img,buttonX,buttonY,ctx,button,(button.cb===undefined)?ITEM_DIRECTION:0) } } buttonX+=47 } if (mouseOverButton!==undefined) { DisplayTooltip(mouseOverButton,ctx) } if (!mousedOver) { MOUSEOVERTIME=-1 } if (gridModeIsAvailable()) { AddButton(MOVE_BUTTON.img,MOVE_BUTTON.x,MOVE_BUTTON.y,ctx,MOVE_BUTTON,MOVE_BUTTON.cb) } } } function gridModeIsAvailable() { return (gameGrid.length>5||(gameGrid.length>0&&gameGrid[0].length>5))&&!DELETEMODE } function AddButton(img,x,y,ctx,button,dir=0) { ctx.fillStyle="#b5b5b5" ctx.fillRect(x,y,32,32) if (img===ID_WRITER) { drawImage(x+16,y+16,img,ctx,dir*90-90) } else { drawImage(x+16,y+16,img,ctx,dir*90) } } function ConsumeTape() { BOT_TAPE=BOT_TAPE.substring(1) } function AppendTape(col) { BOT_TAPE+=col } function OverwriteTape(col) { BOT_TAPE=col+BOT_TAPE.substring(1) } function LeftOf(dir) { return (dir+1)%4 } function RightOf(dir) { if (dir===0) {dir=4} return (dir-1)%4 }