Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| var keyRepeatTimer=0; | |
| var keyRepeatIndex=0; | |
| var input_throttle_timer=0.0; | |
| var lastinput=-100; | |
| var dragging=false; | |
| var rightdragging=false; | |
| var columnAdded=false; | |
| function selectText(containerid,e) { | |
| e = e || window.event; | |
| var myspan = document.getElementById(containerid); | |
| if (e&&(e.ctrlKey || e.metaKey)) { | |
| var levelarr = ["console"].concat(myspan.innerHTML.split("<br>")); | |
| var leveldat = levelFromString(state,levelarr); | |
| loadLevelFromLevelDat(state,leveldat,null); | |
| canvasResize(); | |
| } else { | |
| if (document.selection) { | |
| var range = document.body.createTextRange(); | |
| range.moveToElementText(myspan); | |
| range.select(); | |
| } else if (window.getSelection) { | |
| var range = document.createRange(); | |
| range.selectNode(myspan); | |
| window.getSelection().addRange(range); | |
| } | |
| } | |
| } | |
| function recalcLevelBounds(){ | |
| } | |
| function arrCopy(from, fromoffset, to, tooffset, len) { | |
| while (len--) | |
| to[tooffset++] = from[fromoffset]++; | |
| } | |
| function adjustLevel(level, widthdelta, heightdelta) { | |
| backups.push(backupLevel()); | |
| var oldlevel = level.clone(); | |
| level.width += widthdelta; | |
| level.height += heightdelta; | |
| level.n_tiles = level.width * level.height; | |
| level.objects = new Int32Array(level.n_tiles * STRIDE_OBJ); | |
| var bgMask = new BitVec(STRIDE_OBJ); | |
| bgMask.ibitset(state.backgroundid); | |
| for (var i=0; i<level.n_tiles; ++i) | |
| level.setCell(i, bgMask); | |
| level.movements = new Int32Array(level.objects.length); | |
| columnAdded=true; | |
| RebuildLevelArrays(); | |
| return oldlevel; | |
| } | |
| function addLeftColumn() { | |
| var oldlevel = adjustLevel(level, 1, 0); | |
| for (var x=1; x<level.width; ++x) { | |
| for (var y=0; y<level.height; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index - level.height)) | |
| } | |
| } | |
| } | |
| function addRightColumn() { | |
| var oldlevel = adjustLevel(level, 1, 0); | |
| for (var x=0; x<level.width-1; ++x) { | |
| for (var y=0; y<level.height; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index)) | |
| } | |
| } | |
| } | |
| function addTopRow() { | |
| var oldlevel = adjustLevel(level, 0, 1); | |
| for (var x=0; x<level.width; ++x) { | |
| for (var y=1; y<level.height; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index - x - 1)) | |
| } | |
| } | |
| } | |
| function addBottomRow() { | |
| var oldlevel = adjustLevel(level, 0, 1); | |
| for (var x=0; x<level.width; ++x) { | |
| for (var y=0; y<level.height - 1; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index - x)); | |
| } | |
| } | |
| } | |
| function removeLeftColumn() { | |
| if (level.width<=1) { | |
| return; | |
| } | |
| var oldlevel = adjustLevel(level, -1, 0); | |
| for (var x=0; x<level.width; ++x) { | |
| for (var y=0; y<level.height; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index + level.height)) | |
| } | |
| } | |
| } | |
| function removeRightColumn(){ | |
| if (level.width<=1) { | |
| return; | |
| } | |
| var oldlevel = adjustLevel(level, -1, 0); | |
| for (var x=0; x<level.width; ++x) { | |
| for (var y=0; y<level.height; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index)) | |
| } | |
| } | |
| } | |
| function removeTopRow(){ | |
| if (level.height<=1) { | |
| return; | |
| } | |
| var oldlevel = adjustLevel(level, 0, -1); | |
| for (var x=0; x<level.width; ++x) { | |
| for (var y=0; y<level.height; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index + x + 1)) | |
| } | |
| } | |
| } | |
| function removeBottomRow(){ | |
| if (level.height<=1) { | |
| return; | |
| } | |
| var oldlevel = adjustLevel(level, 0, -1); | |
| for (var x=0; x<level.width; ++x) { | |
| for (var y=0; y<level.height; ++y) { | |
| var index = x*level.height + y; | |
| level.setCell(index, oldlevel.getCell(index + x)) | |
| } | |
| } | |
| } | |
| function matchGlyph(inputmask,glyphAndMask) { | |
| // find mask with closest match | |
| var highestbitcount=-1; | |
| var highestmask; | |
| for (var i=0; i<glyphAndMask.length; ++i) { | |
| var glyphname = glyphAndMask[i][0]; | |
| var glyphmask = glyphAndMask[i][1]; | |
| var glyphbits = glyphAndMask[i][2]; | |
| //require all bits of glyph to be in input | |
| if (glyphmask.bitsSetInArray(inputmask.data)) { | |
| var bitcount = 0; | |
| for (var bit=0;bit<32*STRIDE_OBJ;++bit) { | |
| if (glyphbits.get(bit) && inputmask.get(bit)) | |
| bitcount++; | |
| if (glyphmask.get(bit) && inputmask.get(bit)) | |
| bitcount++; | |
| } | |
| if (bitcount>highestbitcount) { | |
| highestbitcount=bitcount; | |
| highestmask=glyphname; | |
| } | |
| } | |
| } | |
| if (highestbitcount>0) { | |
| return highestmask; | |
| } | |
| logErrorNoLine("Wasn't able to approximate a glyph value for some tiles, using '.' as a placeholder.",true); | |
| return '.'; | |
| } | |
| var htmlEntityMap = { | |
| "&": "&", | |
| "<": "<", | |
| ">": ">", | |
| '"': '"', | |
| "'": ''', | |
| "/": '/' | |
| }; | |
| var selectableint = 0; | |
| function printLevel() { | |
| var glyphMasks = []; | |
| for (var glyphName in state.glyphDict) { | |
| if (state.glyphDict.hasOwnProperty(glyphName)&&glyphName.length===1) { | |
| var glyph = state.glyphDict[glyphName]; | |
| var glyphmask=new BitVec(STRIDE_OBJ); | |
| for (var i=0;i<glyph.length;i++) | |
| { | |
| var id = glyph[i]; | |
| if (id>=0) { | |
| glyphmask.ibitset(id); | |
| } | |
| } | |
| var glyphbits = glyphmask.clone(); | |
| //register the same - backgroundmask with the same name | |
| var bgMask = state.layerMasks[state.backgroundlayer]; | |
| glyphmask.iclear(bgMask); | |
| glyphMasks.push([glyphName, glyphmask, glyphbits]); | |
| } | |
| } | |
| selectableint++; | |
| var tag = 'selectable'+selectableint; | |
| var output="Printing level contents:<br><br><span id=\""+tag+"\" onclick=\"selectText('"+tag+"',event)\">"; | |
| cache_console_messages = false; | |
| for (var j=0;j<level.height;j++) { | |
| for (var i=0;i<level.width;i++) { | |
| var cellIndex = j+i*level.height; | |
| var cellMask = level.getCell(cellIndex); | |
| var glyph = matchGlyph(cellMask,glyphMasks); | |
| if (glyph in htmlEntityMap) { | |
| glyph = htmlEntityMap[glyph]; | |
| } | |
| output = output+glyph; | |
| } | |
| if (j<level.height-1){ | |
| output=output+"<br>"; | |
| } | |
| } | |
| output+="</span><br><br>" | |
| consolePrint(output,true); | |
| } | |
| function levelEditorClick(event,click) { | |
| if (mouseCoordY<=-2) { | |
| var ypos = editorRowCount-(-mouseCoordY-2)-1; | |
| var newindex=mouseCoordX+(screenwidth-1)*ypos; | |
| if (mouseCoordX===-1) { | |
| printLevel(); | |
| } else if (mouseCoordX>=0&&newindex<glyphImages.length) { | |
| glyphSelectedIndex=newindex; | |
| redraw(); | |
| } | |
| } else if (mouseCoordX>-1&&mouseCoordY>-1&&mouseCoordX<screenwidth-2&&mouseCoordY<screenheight-2-editorRowCount ) { | |
| var glyphname = glyphImagesCorrespondance[glyphSelectedIndex]; | |
| var glyph = state.glyphDict[glyphname]; | |
| var glyphmask = new BitVec(STRIDE_OBJ); | |
| for (var i=0;i<glyph.length;i++) | |
| { | |
| var id = glyph[i]; | |
| if (id>=0) { | |
| glyphmask.ibitset(id); | |
| } | |
| } | |
| var backgroundMask = state.layerMasks[state.backgroundlayer]; | |
| if (glyphmask.bitsClearInArray(backgroundMask.data)) { | |
| // If we don't already have a background layer, mix in | |
| // the default one. | |
| glyphmask.ibitset(state.backgroundid); | |
| } | |
| var coordIndex = mouseCoordY + mouseCoordX*level.height; | |
| var getcell = level.getCell(coordIndex); | |
| if (getcell.equals(glyphmask)) { | |
| return; | |
| } else { | |
| if (anyEditsSinceMouseDown===false) { | |
| anyEditsSinceMouseDown=true; | |
| backups.push(backupLevel()); | |
| } | |
| level.setCell(coordIndex, glyphmask); | |
| redraw(); | |
| } | |
| } | |
| else if (click) { | |
| if (mouseCoordX===-1) { | |
| //add a left row to the map | |
| addLeftColumn(); | |
| canvasResize(); | |
| } else if (mouseCoordX===screenwidth-2) { | |
| addRightColumn(); | |
| canvasResize(); | |
| } | |
| if (mouseCoordY===-1) { | |
| addTopRow(); | |
| canvasResize(); | |
| } else if (mouseCoordY===screenheight-2-editorRowCount) { | |
| addBottomRow(); | |
| canvasResize(); | |
| } | |
| } | |
| } | |
| function levelEditorRightClick(event,click) { | |
| if (mouseCoordY===-2) { | |
| if (mouseCoordX<=glyphImages.length) { | |
| glyphSelectedIndex=mouseCoordX; | |
| redraw(); | |
| } | |
| } else if (mouseCoordX>-1&&mouseCoordY>-1&&mouseCoordX<screenwidth-2&&mouseCoordY<screenheight-2-editorRowCount ) { | |
| var coordIndex = mouseCoordY + mouseCoordX*level.height; | |
| var glyphmask = new BitVec(STRIDE_OBJ); | |
| glyphmask.ibitset(state.backgroundid); | |
| level.setCell(coordIndex, glyphmask); | |
| redraw(); | |
| } | |
| else if (click) { | |
| if (mouseCoordX===-1) { | |
| //add a left row to the map | |
| removeLeftColumn(); | |
| canvasResize(); | |
| } else if (mouseCoordX===screenwidth-2) { | |
| removeRightColumn(); | |
| canvasResize(); | |
| } | |
| if (mouseCoordY===-1) { | |
| removeTopRow(); | |
| canvasResize(); | |
| } else if (mouseCoordY===screenheight-2-editorRowCount) { | |
| removeBottomRow(); | |
| canvasResize(); | |
| } | |
| } | |
| } | |
| var anyEditsSinceMouseDown = false; | |
| function onMouseDown(event) { | |
| if (event.button===0 && !(event.ctrlKey||event.metaKey) ) { | |
| lastDownTarget = event.target; | |
| keybuffer=[]; | |
| if (event.target===canvas) { | |
| setMouseCoord(event); | |
| dragging=true; | |
| rightdragging=false; | |
| if (levelEditorOpened) { | |
| anyEditsSinceMouseDown=false; | |
| return levelEditorClick(event,true); | |
| } | |
| } | |
| dragging=false; | |
| rightdragging=false; | |
| } else if (event.button===2 || (event.button===0 && (event.ctrlKey||event.metaKey)) ) { | |
| if (event.target.id==="gameCanvas") { | |
| dragging=false; | |
| rightdragging=true; | |
| if (levelEditorOpened) { | |
| return levelEditorRightClick(event,true); | |
| } | |
| } | |
| } | |
| } | |
| function rightClickCanvas(event) { | |
| return prevent(event); | |
| } | |
| function onMouseUp(event) { | |
| dragging=false; | |
| rightdragging=false; | |
| } | |
| function onKeyDown(event) { | |
| event = event || window.event; | |
| // Prevent arrows/space from scrolling page | |
| if ((!IDE) && ([32, 37, 38, 39, 40].indexOf(event.keyCode) > -1)) { | |
| prevent(event); | |
| } | |
| if ((!IDE) && event.keyCode===77){//m | |
| toggleMute(); | |
| } | |
| if (keybuffer.indexOf(event.keyCode)>=0) { | |
| return; | |
| } | |
| if(lastDownTarget === canvas || (window.Mobile && (lastDownTarget === window.Mobile.focusIndicator) ) ){ | |
| if (keybuffer.indexOf(event.keyCode)===-1) { | |
| keybuffer.splice(keyRepeatIndex,0,event.keyCode); | |
| keyRepeatTimer=0; | |
| checkKey(event,true); | |
| } | |
| } | |
| if (canDump===true) { | |
| if (event.keyCode===74 && (event.ctrlKey||event.metaKey)) {//ctrl+j | |
| dumpTestCase(); | |
| prevent(event); | |
| } else if (event.keyCode===75 && (event.ctrlKey||event.metaKey)) {//ctrl+k | |
| makeGIF(); | |
| prevent(event); | |
| } else if (event.keyCode===83 && (event.ctrlKey||event.metaKey)) {//ctrl+s | |
| saveClick(); | |
| prevent(event); | |
| } | |
| } | |
| } | |
| function relMouseCoords(event){ | |
| var totalOffsetX = 0; | |
| var totalOffsetY = 0; | |
| var canvasX = 0; | |
| var canvasY = 0; | |
| var currentElement = this; | |
| do{ | |
| totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft; | |
| totalOffsetY += currentElement.offsetTop - currentElement.scrollTop; | |
| } | |
| while(currentElement = currentElement.offsetParent) | |
| canvasX = event.pageX - totalOffsetX; | |
| canvasY = event.pageY - totalOffsetY; | |
| return {x:canvasX, y:canvasY} | |
| } | |
| HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords; | |
| function onKeyUp(event) { | |
| event = event || window.event; | |
| var index=keybuffer.indexOf(event.keyCode); | |
| if (index>=0){ | |
| keybuffer.splice(index,1); | |
| if (keyRepeatIndex>=index){ | |
| keyRepeatIndex--; | |
| } | |
| } | |
| } | |
| function onMyFocus(event) { | |
| keybuffer=[]; | |
| keyRepeatIndex = 0; | |
| keyRepeatTimer = 0; | |
| } | |
| function onMyBlur(event) { | |
| keybuffer=[]; | |
| keyRepeatIndex = 0; | |
| keyRepeatTimer = 0; | |
| } | |
| var mouseCoordX=0; | |
| var mouseCoordY=0; | |
| function setMouseCoord(e){ | |
| var coords = canvas.relMouseCoords(e); | |
| mouseCoordX=coords.x-xoffset; | |
| mouseCoordY=coords.y-yoffset; | |
| mouseCoordX=Math.floor(mouseCoordX/cellwidth); | |
| mouseCoordY=Math.floor(mouseCoordY/cellheight); | |
| } | |
| function mouseMove(event) { | |
| if (levelEditorOpened) { | |
| setMouseCoord(event); | |
| if (dragging) { | |
| levelEditorClick(event,false); | |
| } else if (rightdragging){ | |
| levelEditorRightClick(event,false); | |
| } | |
| redraw(); | |
| } | |
| //window.console.log("showcoord ("+ canvas.width+","+canvas.height+") ("+x+","+y+")"); | |
| } | |
| function mouseOut() { | |
| // window.console.log("clear"); | |
| } | |
| document.addEventListener('mousedown', onMouseDown, false); | |
| document.addEventListener('mouseup', onMouseUp, false); | |
| document.addEventListener('keydown', onKeyDown, false); | |
| document.addEventListener('keyup', onKeyUp, false); | |
| window.addEventListener('focus', onMyFocus, false); | |
| window.addEventListener('blur', onMyBlur, false); | |
| function prevent(e) { | |
| if (e.preventDefault) e.preventDefault(); | |
| if (e.stopImmediatePropagation) e.stopImmediatePropagation(); | |
| if (e.stopPropagation) e.stopPropagation(); | |
| e.returnValue=false; | |
| return false; | |
| } | |
| function checkKey(e,justPressed) { | |
| if (winning) { | |
| return; | |
| } | |
| var inputdir=-1; | |
| switch(e.keyCode) { | |
| case 65://a | |
| case 37: //left | |
| { | |
| // window.console.log("LEFT"); | |
| inputdir=1; | |
| break; | |
| } | |
| case 38: //up | |
| case 87: //w | |
| { | |
| // window.console.log("UP"); | |
| inputdir=0; | |
| break; | |
| } | |
| case 68://d | |
| case 39: //right | |
| { | |
| // window.console.log("RIGHT"); | |
| inputdir=3; | |
| break; | |
| } | |
| case 83://s | |
| case 40: //down | |
| { | |
| // window.console.log("DOWN"); | |
| inputdir=2; | |
| break; | |
| } | |
| case 80://p | |
| { | |
| printLevel(); | |
| break; | |
| } | |
| case 13://enter | |
| case 32://space | |
| case 67://c | |
| case 88://x | |
| { | |
| // window.console.log("ACTION"); | |
| if (norepeat_action===false || justPressed) { | |
| inputdir=4; | |
| } else { | |
| return; | |
| } | |
| break; | |
| } | |
| case 85://u | |
| case 90://z | |
| { | |
| //undo | |
| if (textMode===false) { | |
| pushInput("undo"); | |
| DoUndo(false,true); | |
| canvasResize(); // calls redraw | |
| return prevent(e); | |
| } | |
| break; | |
| } | |
| case 82://r | |
| { | |
| if (textMode===false) { | |
| if (justPressed) { | |
| pushInput("restart"); | |
| DoRestart(); | |
| canvasResize(); // calls redraw | |
| return prevent(e); | |
| } | |
| } | |
| break; | |
| } | |
| case 27://escape | |
| { | |
| if (titleScreen===false) { | |
| goToTitleScreen(); | |
| tryPlayTitleSound(); | |
| canvasResize(); | |
| return prevent(e) | |
| } | |
| break; | |
| } | |
| case 69: {//e | |
| if (canOpenEditor) { | |
| if (justPressed) { | |
| if (titleScreen){ | |
| if (state.title==="EMPTY GAME"){ | |
| compile(["loadFirstNonMessageLevel"]); | |
| } else { | |
| nextLevel(); | |
| } | |
| } | |
| levelEditorOpened=!levelEditorOpened; | |
| if (levelEditorOpened===false){ | |
| printLevel(); | |
| } | |
| restartTarget=backupLevel(); | |
| canvasResize(); | |
| } | |
| return prevent(e); | |
| } | |
| break; | |
| } | |
| case 48://0 | |
| case 49://1 | |
| case 50://2 | |
| case 51://3 | |
| case 52://4 | |
| case 53://5 | |
| case 54://6 | |
| case 55://7 | |
| case 56://8 | |
| case 57://9 | |
| { | |
| if (levelEditorOpened&&justPressed) { | |
| var num=9; | |
| if (e.keyCode>=49) { | |
| num = e.keyCode-49; | |
| } | |
| if (num<glyphImages.length) { | |
| glyphSelectedIndex=num; | |
| } else { | |
| consolePrint("Trying to select tile outside of range in level editor.",true) | |
| } | |
| canvasResize(); | |
| return prevent(e); | |
| } | |
| break; | |
| } | |
| } | |
| if (throttle_movement && inputdir>=0&&inputdir<=3) { | |
| if (lastinput==inputdir && input_throttle_timer<repeatinterval) { | |
| return; | |
| } else { | |
| lastinput=inputdir; | |
| input_throttle_timer=0; | |
| } | |
| } | |
| if (textMode) { | |
| if (state.levels.length===0) { | |
| //do nothing | |
| } else if (titleScreen) { | |
| if (titleMode===0) { | |
| if (inputdir===4&&justPressed) { | |
| if (titleSelected===false) { | |
| tryPlayStartGameSound(); | |
| titleSelected=true; | |
| messageselected=false; | |
| timer=0; | |
| quittingTitleScreen=true; | |
| generateTitleScreen(); | |
| canvasResize(); | |
| } | |
| } | |
| } else { | |
| if (inputdir==4&&justPressed) { | |
| if (titleSelected===false) { | |
| tryPlayStartGameSound(); | |
| titleSelected=true; | |
| messageselected=false; | |
| timer=0; | |
| quittingTitleScreen=true; | |
| generateTitleScreen(); | |
| redraw(); | |
| } | |
| } | |
| else if (inputdir===0||inputdir===2) { | |
| if (inputdir===0){ | |
| titleSelection=0; | |
| } else { | |
| titleSelection=1; | |
| } | |
| generateTitleScreen(); | |
| redraw(); | |
| } | |
| } | |
| } else { | |
| if (inputdir==4&&justPressed) { | |
| if (unitTesting) { | |
| nextLevel(); | |
| return; | |
| } else if (messageselected===false) { | |
| messageselected=true; | |
| timer=0; | |
| quittingMessageScreen=true; | |
| tryPlayCloseMessageSound(); | |
| titleScreen=false; | |
| drawMessageScreen(); | |
| } | |
| } | |
| } | |
| } else { | |
| if (!againing && inputdir>=0) { | |
| if (inputdir===4 && ('noaction' in state.metadata)) { | |
| } else { | |
| pushInput(inputdir); | |
| if (processInput(inputdir)) { | |
| redraw(); | |
| } | |
| } | |
| return prevent(e); | |
| } | |
| } | |
| } | |
| function update() { | |
| timer+=deltatime; | |
| input_throttle_timer+=deltatime; | |
| if (quittingTitleScreen) { | |
| if (timer/1000>0.3) { | |
| quittingTitleScreen=false; | |
| nextLevel(); | |
| } | |
| } | |
| if (againing) { | |
| if (timer>againinterval&&messagetext.length==0) { | |
| if (processInput(-1)) { | |
| redraw(); | |
| keyRepeatTimer=0; | |
| autotick=0; | |
| } | |
| } | |
| } | |
| if (quittingMessageScreen) { | |
| if (timer/1000>0.15) { | |
| quittingMessageScreen=false; | |
| if (messagetext==="") { | |
| nextLevel(); | |
| } else { | |
| messagetext=""; | |
| textMode=false; | |
| titleScreen=false; | |
| titleMode=(curlevel>0||curlevelTarget!==null)?1:0; | |
| titleSelected=false; | |
| titleSelection=0; | |
| canvasResize(); | |
| checkWin(); | |
| } | |
| } | |
| } | |
| if (winning) { | |
| if (timer/1000>0.5) { | |
| winning=false; | |
| nextLevel(); | |
| } | |
| } | |
| if (keybuffer.length>0) { | |
| keyRepeatTimer+=deltatime; | |
| var ticklength = throttle_movement ? repeatinterval : repeatinterval/(Math.sqrt(keybuffer.length)); | |
| if (keyRepeatTimer>ticklength) { | |
| keyRepeatTimer=0; | |
| keyRepeatIndex = (keyRepeatIndex+1)%keybuffer.length; | |
| var key = keybuffer[keyRepeatIndex]; | |
| checkKey({keyCode:key},false); | |
| } | |
| } | |
| if (autotickinterval>0&&!textMode&&!levelEditorOpened&&!againing&&!winning) { | |
| autotick+=deltatime; | |
| if (autotick>autotickinterval) { | |
| autotick=0; | |
| pushInput("tick"); | |
| if (processInput(-1)) { | |
| redraw(); | |
| } | |
| } | |
| } | |
| } | |
| // Lights, camera…function! | |
| setInterval(function() { | |
| update(); | |
| }, deltatime); |