diff --git a/README.md b/README.md index a94a5d3..1203650 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Lode Runner - Total Recall +Lode Runner - Total Recall ======================================= ## (超級運動員 - 全面回憶) @@ -7,19 +7,19 @@ This program build with Javascript + [CreateJS](http://www.createjs.com). ### * 3 GAME Mode & 1 DEMO Mode - + - + - + - + @@ -28,45 +28,45 @@ This program build with Javascript + [CreateJS](http://www.createjs.com). ### * Include 5 Game Versions
(1) Challenge Mode1. Challenge Mode Compete with other players.
(2) Training Mode2. Training Mode Player can select any levels
(3) Edit Mode3. Edit Mode Player can create custom levels
(4) Demo Mode4. Demo Mode Demo passed levels
- + - + - + - + - +
(1) Classic Lode Runner1. Classic Lode Runner (150 Levels) Difficulty: 3
(2) Professional Lode Runner2. Professional Lode Runner (150 Levels) Difficulty: 4
(3) Revenge of Lode Runner3. Revenge of Lode Runner (17 Levels) Difficulty: 4
(4) Lode Runner Fan Book4. Lode Runner Fan Book (66 Levels) Difficulty: 5
(5) Championship Lode Runner5. Championship Lode Runner (51 Levels) Difficulty: 5
-### * Provide 2 Theme +### * Provide 2 Themes - + - + @@ -76,19 +76,22 @@ This program build with Javascript + [CreateJS](http://www.createjs.com).
(1)1. APPLE-II
(2)2. Commodore 64
- + - +
(1)1. Repeat Actions On: Like APPLE-II keyboard behavior
(2)2. Repeat Actions Off: Like NES keyboard behavior
- -### * PLAY -https://loderunnerwebgame.com/game/ - +### * Play on a Local Machine +#### A simple web server is required to play LoderRunner on a local machine; follow these instructions for Windows: +1. Download and extract the source code into a directory. +2. If Python is installed, open a command prompt, navigate to the LodeRunner directory, and run "`python -m http.server 8080`" to start an HTTP server. +3. Alternatively, download `sswws.zip` from [here](https://github.com/faustinoaq/sswws/releases/tag/v0.1.0), extract it into the LodeRunner directory, and run "`sswws.exe -p 8080`" from the command prompt. +4. Open a web browser and go to "`http://127.0.0.1:8080/lodeRunner.html`". +##### Note: Changing the HTTP port will cause the browser to store "status" or "custom levels" data in a separate storage. ------------------------------------ diff --git a/image/demo.png b/image/demo.png index bf984fc..1632755 100644 Binary files a/image/demo.png and b/image/demo.png differ diff --git a/image/nextMap.png b/image/nextMap.png new file mode 100644 index 0000000..fd59116 Binary files /dev/null and b/image/nextMap.png differ diff --git a/image/openFolder.png b/image/openFolder.png new file mode 100644 index 0000000..481ac9f Binary files /dev/null and b/image/openFolder.png differ diff --git a/image/paste.png b/image/paste.png new file mode 100644 index 0000000..fa322cb Binary files /dev/null and b/image/paste.png differ diff --git a/image/prevMap.png b/image/prevMap.png new file mode 100644 index 0000000..0ef858f Binary files /dev/null and b/image/prevMap.png differ diff --git a/lodeRunner.colorTheme.js b/lodeRunner.colorTheme.js index df46005..a543916 100644 --- a/lodeRunner.colorTheme.js +++ b/lodeRunner.colorTheme.js @@ -141,12 +141,19 @@ function changeColor(canvas, oldColor, newColor) var data = imgData.data; var bitChanged = 0; + // add a range compare for fix browser (Brave) color have bit different ?, 6/3/2021 + var oldMin = [oldColor[0]-2, oldColor[1]-2, oldColor[2]-2]; + var oldMax = [oldColor[0]+2, oldColor[1]+2, oldColor[2]+2]; + for (var i = 0; i < data.length; i += 4) { var red = data[i + 0]; var green = data[i + 1]; var blue = data[i + 2]; var alpha = data[i + 3]; - if(oldColor[0] == red && oldColor[1] == green && oldColor[2] == blue) { + if(oldMin[0] <= red && red <= oldMax[0] && + oldMin[1] <= green && green <= oldMax[1] && + oldMin[2] <= blue && blue <= oldMax[2]) + { data[i+0] = newColor[0]; data[i+1] = newColor[1]; data[i+2] = newColor[2]; diff --git a/lodeRunner.def.js b/lodeRunner.def.js index 9407f06..461ad18 100644 --- a/lodeRunner.def.js +++ b/lodeRunner.def.js @@ -1,4 +1,4 @@ -var VERSION = "2.21i"; +var VERSION = "2.30e"; var AI_VERSION = 4; var NO_OF_TILES_X = 28, @@ -27,7 +27,7 @@ var KEYCODE_BKSPACE = 8, //backspace KEYCODE_ESC = 27, //escape KEYCODE_X = 88, //dig right - KEYCODE_Y = 89, //dig left (for German keyboards) + KEYCODE_Y = 89, //dig left (Y key to dig left, for German keyboards) KEYCODE_Z = 90, //dig left KEYCODE_LEFT = 37, //move left @@ -75,10 +75,11 @@ var KEYCODE_0 = 48, KEYCODE_8 = 56, KEYCODE_9 = 57; -var SCORE_COMPLETE_LEVEL = 1500, SCORE_COUNTER = 15; +var SCORE_COMPLETE_LEVEL = 1500, SCORE_COUNTER = 15, SCORE_GET_GOLD = 250, SCORE_IN_HOLE = 75, - SCORE_GUARD_DEAD = 75; + SCORE_GUARD_DEAD = 75, + SCORE_WIN_PER_MAN = 42500; var SCORE_VALUE_PER_POINT = 100; //for modern & edit mode @@ -91,7 +92,8 @@ var GAME_START = 0, GAME_RUNNING = 1, GAME_NEW_LEVEL = 6, GAME_RUNNER_DEAD = 7, GAME_OVER_ANIMATION = 8, GAME_OVER = 9, GAME_NEXT_LEVEL = 10, GAME_PREV_LEVEL = 11, - GAME_LOADING = 12, GAME_WIN = 13; + GAME_LOADING = 12, GAME_WIN_SCORE_COUNT = 13, + GAME_WIN = 14; var ACT_UNKNOWN = -1, ACT_STOP = 0, ACT_LEFT = 1, ACT_RIGHT = 2, @@ -181,6 +183,8 @@ var STORAGE_TEST_LEVEL = "loderunner_testlevel"; var STORAGE_HISCORE_INFO = "loderunner_hiScore"; +var STORAGE_LASTSCORE = "loderunner_lastScore"; + var STORAGE_PLAYER_NAME = "loderunner_player"; var STORAGE_UID = "loderunner_uid"; @@ -189,3 +193,8 @@ var STORAGE_THEME_COLOR = "loderunner_color_"; var STORAGE_REPEAT_ACTION = "loderunner_actRepeat"; var STORAGE_GAMEPAD_MODE = "loderunner_gamepadMode"; + +//================================ +// backup & restore file header +//================================ +var LRWG_FILE_START_INFO = "LODE RUNNER WEB GAME."; diff --git a/lodeRunner.demo.js b/lodeRunner.demo.js index 2471694..e0ec858 100644 --- a/lodeRunner.demo.js +++ b/lodeRunner.demo.js @@ -135,7 +135,7 @@ function setDemoData(jsonTxt) playerDemoData[wDemoData[i].level-1] = wDemoData[i]; } } else { - error(arguments.callee.name, "Wrong Demo Data: " + jsonTxt); + error("Wrong Demo Data: " + jsonTxt); } demoDataLoading = 0; if(playMode == PLAY_MODERN && gameState == GAME_START ) demoIconObj.enable(); @@ -175,7 +175,7 @@ function getDemoInfo() //demoData = playerDemoData[playData-1]; demoData = playerDemoData; } else { - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); } if(infoJSON == null) { @@ -196,7 +196,7 @@ function setDemoInfo() if (playData >= 1 && playData <= maxPlayId) { setStorage(STORAGE_DEMO_INFO + playData, infoJSON); } else { - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); } } @@ -217,7 +217,7 @@ function getNextDemoLevel() function curDemoLevelIsVaild() { if(playData == PLAY_DATA_USERDEF) return 0; - return (typeof playerDemoData[curLevel-1] != "undefined"); + return (playerDemoData.length >= curLevel && typeof playerDemoData[curLevel-1] != "undefined"); } function updatePlayerDemoData(playData, demoDataInfo) @@ -258,7 +258,7 @@ function respUpdatePlayerDemoData(jsonTxt) playerDemoData[level-1].cId = respObj.cId; } } else { - error(arguments.callee.name, "Wrong resp Data: " + jsonTxt); + error("Wrong resp Data: " + jsonTxt); } } @@ -471,7 +471,7 @@ function convertBornPos() bornRecord = tmpRecord; } -var curDemoData; +var curDemoData = { ai: AI_VERSION, time: 0 }; //bug fixed: when press CTRL-C without demo data function dumpRecord() { var txtStr; diff --git a/lodeRunner.edit.js b/lodeRunner.edit.js index 0039415..2128dc4 100644 --- a/lodeRunner.edit.js +++ b/lodeRunner.edit.js @@ -50,6 +50,8 @@ var editorTile =[], editorActiveTileId = -1; var editorButton = [], editorButtonMouseOverId = -1; var editInNarrowScreen = 0; +var editMapIsEmpty = 1; + function startEditMode() { playMode = PLAY_EDIT; @@ -69,6 +71,7 @@ function startEditMode() createBaseTile(); createEditMap(); + checkEditMapEmpty(); // 2021/04 startEditTicker(); setButtonState(); initForPlay(); @@ -116,6 +119,7 @@ function canvasEditReSize() canvas.style.left = (left>0?left:0) + "px"; canvas.style.top = (top>0?top:0) + "px"; canvas.style.position = "absolute"; + canvas.style.cursor = "default"; tileWScale = BASE_TILE_X * tileScale; tileHScale = BASE_TILE_Y * tileScale; @@ -208,6 +212,7 @@ function clearEditMap() tileObj.id = emptyTile.id; } } + editMapIsEmpty = 1; } function tile2Id(tileChar) @@ -453,6 +458,7 @@ function drawNewButton() } else { disableTestButton(); } + setPasteIconState(); clearTestLevel(); testLevelInfo.fromPlayData = -1; testLevelInfo.fromLevel = -1; @@ -577,7 +583,7 @@ function drawLoadButton() function loadButtonClick() { saveState(); - if(!editMapIsEmpty()) { + if(!editMapIsEmpty) { yesNoDialog(["Abort current editing ?"], yesBitmap, noBitmap, mainStage, tileScale, loadExistLevel); } else { loadExistLevel(1); @@ -804,7 +810,7 @@ function stageMouseDown(event) mouseDown = 1; //console.log(e.which) //console.log("DOWN"); - } + } } function stageMouseUp(event) @@ -895,7 +901,7 @@ function delManCheck(id, x, y) guardTile.id = emptyTile.id; lastGuardList.splice(removeId,1); } else { - error(arguments.callee.name, "design error !"); + error("design error !"); } break; } @@ -905,26 +911,35 @@ function delManCheck(id, x, y) function editSelectMenuClose(levelDeleted, newLevel, state) { if(levelDeleted) { - switch(true) { - case (state > 0): //level changed - getTestLevel(testLevelInfo); - testLevelInfo.level = newLevel; - setTestLevel(testLevelInfo); + + setEditSelectMenu(); + startEditMode(); +/* + Bug fixed: If player delete some custom levels and restart the program immediate will cause + testLevel inconsistent, must del or shift testlevel immediate. + ==> so move below statements into function delLevel(level). + + switch(true) { + case (state > 0): //level number changed + //getTestLevel(testLevelInfo); + //testLevelInfo.level = newLevel; + //setTestLevel(testLevelInfo); startEditMode(); break; case (state < 0): //level deleted - clearTestLevel(); + //clearTestLevel(); setEditSelectMenu(); startEditMode(); break; - case (state == 0 && newLevel == 0): //new level - if(testLevelInfo.modified == 1) { - testLevelInfo.level = editLevels+1; - setTestLevel(testLevelInfo); - } + case (state == 0 && newLevel == 0): //newLevel = 0 : meanings edit new level just need change level id + //if(testLevelInfo.modified == 1) { + // testLevelInfo.level = editLevels+1; + // setTestLevel(testLevelInfo); + //} startEditMode(); break; } +*/ } } @@ -949,14 +964,28 @@ function map2LevelData() } } -function editMapIsEmpty() +function checkEditMapEmpty() { + editMapIsEmpty = 1; + + mapCheckLoop: for(var y = 0; y < NO_OF_TILES_Y; y++) { for(var x = 0; x < NO_OF_TILES_X; x++) { - if(editMap[x][y].id != emptyTile.id) return 0; + if(editMap[x][y].id != emptyTile.id) { + editMapIsEmpty = 0; + break mapCheckLoop; + } } } - return 1; + setPasteIconState(); +} + +function setPasteIconState() +{ + if (editMapIsEmpty && copyLevelMap != null && testLevelInfo.level <= MAX_EDIT_LEVEL) + pasteIconObj.enable(); + else + pasteIconObj.disable(); } function copyEditingMap() @@ -977,7 +1006,7 @@ function copyEditingMap() var editWarningText = null; function editWarningMsg(hidden) { - var x, y, width, height; + var width, height; if(editWarningText == null) editWarningText = new createjs.Text("Too many custom levels !", @@ -985,9 +1014,9 @@ function editWarningMsg(hidden) width = editWarningText.getBounds().width; height = editWarningText.getBounds().height; - x = editWarningText.x = (NO_OF_TILES_X*(tileWScale+EDIT_PADDING) - width) / 2 | 0; - y = editWarningText.y = (NO_OF_TILES_Y*tileHScale - height) / 2 | 0; - editWarningText.shadow = new createjs.Shadow("white", 2, 2, 1); + editWarningText.x = ((NO_OF_TILES_X+2)*(tileWScale+EDIT_PADDING) - width) / 2 | 0; + editWarningText.y = (NO_OF_TILES_Y*tileHScale - height) / 2 | 0; + editWarningText.shadow = new createjs.Shadow("white", tileScale, 2*tileScale, 1); if(hidden) { mainStage.removeChild(editWarningText); @@ -1005,20 +1034,15 @@ function editHandleKeyDown(event) if (event.ctrlKey) { switch(event.keyCode) { case KEYCODE_C: //CTRL-C : copy current level - copyLevelMap = copyEditingMap(); - copyLevelPassed = (!testLevelInfo.modified && lastRunner) || testLevelInfo.pass; - setTimeout(function() { showTipsText("COPY MAP", 1500);}, 50); + if (!editMapIsEmpty) { + copyLevelMap = copyEditingMap(); + copyLevelPassed = (!testLevelInfo.modified && lastRunner) || testLevelInfo.pass; + setTimeout(function() { showTipsText("COPY MAP", 1500);}, 50); + } break; case KEYCODE_V: //CTRL-V : paste copy map - if(copyLevelMap != null) { - testLevelInfo.levelMap = copyLevelMap; - testLevelInfo.modified = 1; - testLevelInfo.pass = copyLevelPassed; - testLevelInfo.fromPlayData = testLevelInfo.fromLevel = -1; - setTestLevel(testLevelInfo); - startEditMode(); - ////setButtonState(); - setTimeout(function() { showTipsText("PASTE MAP", 1500);}, 50); + if(copyLevelMap != null && editMapIsEmpty && testLevelInfo.level <= MAX_EDIT_LEVEL) { + editPasteMap(); } break; } @@ -1026,6 +1050,19 @@ function editHandleKeyDown(event) return true; } +function editPasteMap() +{ + testLevelInfo.levelMap = copyLevelMap; + testLevelInfo.modified = 1; + testLevelInfo.pass = copyLevelPassed; + testLevelInfo.fromPlayData = testLevelInfo.fromLevel = -1; + setTestLevel(testLevelInfo); + copyLevelMap = null; //clear copy map after paste + startEditMode(); + ////setButtonState(); + setTimeout(function() { showTipsText("PASTE MAP", 1500);}, 50); +} + function checkTileMouseOver(x, y) { var curTile; @@ -1049,6 +1086,7 @@ function selectTileMouseOver(tile) border.graphics.clear(); border.graphics.beginFill("gold").drawRect(-editBorder, -editBorder, tileWScale+editBorder*2, tileHScale+editBorder*2).endFill(); + canvas.style.cursor = "pointer"; //mouseOver = 1; } @@ -1059,6 +1097,7 @@ function selectTileMouseOut(tile) border.graphics.clear(); border.graphics.beginFill(color).drawRect(-editBorder, -editBorder, tileWScale+editBorder*2, tileHScale+editBorder*2).endFill(); + canvas.style.cursor = "default"; //mouseOver = 0; } @@ -1090,7 +1129,8 @@ function editorButtonMouseOver(button) var backColor = button.getChildAt(1); backColor.graphics.clear(); backColor.graphics.beginFill("#ffa").drawRect(0, 0, width, tileHScale).endFill(); - + if (button.alpha) + canvas.style.cursor = "pointer"; //mouseOver = 1; } @@ -1105,7 +1145,7 @@ function editorButtonMouseOut(button) var backColor = button.getChildAt(1); backColor.graphics.clear(); backColor.graphics.beginFill("#fff").drawRect(0, 0, width, tileHScale).endFill(); - + canvas.style.cursor = "default"; //mouseOver = 0; } @@ -1113,8 +1153,10 @@ function setButtonState() { if(testLevelInfo.level > MAX_EDIT_LEVEL) { newButton.alpha = 0; + loadButton.alpha = 0; testButton.alpha = 0; saveButton.alpha = 0; + canvas.style.cursor = "default"; editWarningMsg(0); return; } else { @@ -1141,47 +1183,87 @@ function editTick() var x = (x < 0)?-1:(x|0); var y = ((mainStage.mouseY-EDIT_PADDING) / (tileHScale+EDIT_PADDING) )| 0; - if(testLevelInfo.level > MAX_EDIT_LEVEL) { - return; - } - //debug(mainStage.mouseX,editStartX, x,y); - if(mouseInStage && x >= 0 && x < NO_OF_TILES_X && y >= 0 && y < NO_OF_TILES_Y) { - //edit area + if(testLevelInfo.level <= MAX_EDIT_LEVEL) { + //debug(mainStage.mouseX,editStartX, x,y); + if(mouseInStage && x >= 0 && x < NO_OF_TILES_X && y >= 0 && y < NO_OF_TILES_Y) { + //edit area - if(editorActiveTileId >= 0) selectTileMouseOut(editorTile[editorActiveTileId]); - if(editorButtonMouseOverId >= 0) editorButtonMouseOut(editorButton[editorButtonMouseOverId]); - - if( x != lastDown.x || y != lastDown.y) { - cursorTileObj.alpha = 1; - cursorTileObj.x = (tileWScale + EDIT_PADDING) * x+EDIT_PADDING + editStartX; - cursorTileObj.y = (tileHScale + EDIT_PADDING) * y+EDIT_PADDING; - if(mouseDown) { - var clickTile = editMap[x][y]; - - if(!actTile.id || clickTile.id == actTile.id) { - if(actTile.id) cursorTileObj.alpha = 0.1; - delManCheck(clickTile.id, x, y); - clickTile.tile.getChildAt(1).image = emptyTile.image; - clickTile.id = emptyTile.id; - } else { - delManCheck(clickTile.id, x, y); - clickTile.tile.getChildAt(1).image = actTile.image; - clickTile.id = actTile.id; - addManCheck(actTile.id, x, y); - } - lastDown = {x:x, y:y}; - - if(testLevelInfo.pass || actTile.id == RUNNER_ID || testLevelInfo.modified == 0) { - testLevelInfo.modified = 1; - testLevelInfo.pass = 0; - setButtonState(); + if(editorActiveTileId >= 0) selectTileMouseOut(editorTile[editorActiveTileId]); + if(editorButtonMouseOverId >= 0) editorButtonMouseOut(editorButton[editorButtonMouseOverId]); + + if( x != lastDown.x || y != lastDown.y) { + cursorTileObj.alpha = 1; + cursorTileObj.x = (tileWScale + EDIT_PADDING) * x+EDIT_PADDING + editStartX; + cursorTileObj.y = (tileHScale + EDIT_PADDING) * y+EDIT_PADDING; + if(mouseDown) { + var clickTile = editMap[x][y]; + + if(!actTile.id || clickTile.id == actTile.id) { + if(actTile.id) cursorTileObj.alpha = 0.1; + delManCheck(clickTile.id, x, y); + clickTile.tile.getChildAt(1).image = emptyTile.image; + clickTile.id = emptyTile.id; + } else { + delManCheck(clickTile.id, x, y); + clickTile.tile.getChildAt(1).image = actTile.image; + clickTile.id = actTile.id; + addManCheck(actTile.id, x, y); + } + lastDown = {x:x, y:y}; + checkEditMapEmpty(); + + if(testLevelInfo.pass || actTile.id == RUNNER_ID || testLevelInfo.modified == 0) { + testLevelInfo.modified = 1; + testLevelInfo.pass = 0; + setButtonState(); + } } } + } else { + checkTileMouseOver(mainStage.mouseX, mainStage.mouseY); + checkButtonMouseOver(mainStage.mouseX, mainStage.mouseY); + cursorTileObj.alpha = 0; } - } else { - checkTileMouseOver(mainStage.mouseX, mainStage.mouseY); - checkButtonMouseOver(mainStage.mouseX, mainStage.mouseY); - cursorTileObj.alpha = 0; } mainStage.update(); +} + +function levelMapIsEmpty(levelMap) +{ + for(i =0; i < levelMap.length; i++) { + if (levelMap[i] != ' ') return 0; + } + return 1; +} + +//======================================================== +// +// Copy share level to test level or copy map for editig +// +//========================================================= +function copyShareLevelForEditing() +{ + + getTestLevel(testLevelInfo); + if(testLevelInfo.level <= MAX_EDIT_LEVEL && levelMapIsEmpty(testLevelInfo.levelMap)) { + //test map is empty, replace test map with share level map + testLevelInfo.levelMap = shareLevelData[0]; + testLevelInfo.modified = 1; + testLevelInfo.pass = 1; + testLevelInfo.fromPlayData = testLevelInfo.fromLevel = -1; + setTestLevel(testLevelInfo); + } else { + if (shareLevelData[0] != testLevelInfo.levelMap) { + //(testMap != empty and testMap != shareMap) ==> keep shareLevel map to copyMap + copyLevelMap = shareLevelData[0]; + copyLevelPassed = 1; + } + } + +} + +function editShareLevel() +{ + editEdit(0, null); + } \ No newline at end of file diff --git a/lodeRunner.guard.js b/lodeRunner.guard.js index dec5fe2..4e19911 100644 --- a/lodeRunner.guard.js +++ b/lodeRunner.guard.js @@ -463,7 +463,7 @@ function add2GuardShakeQueue(id, shape) curGuard.curFrameTime = -1; //for init shakingGuardList.push(id); - //error(arguments.callee.name, "push id =" + id + "(" + shakingGuardList + ")" ); + //error("push id =" + id + "(" + shakingGuardList + ")" ); } @@ -488,7 +488,7 @@ function processGuardShake() var id = shakingGuardList[i]; shakingGuardList.splice(i, 1); //remove from list - //error(arguments.callee.name, "remove id =" + id + "(" + shakingGuardList + ")" ); + //error("remove id =" + id + "(" + shakingGuardList + ")" ); climbOut(id); //climb out continue; } @@ -504,11 +504,11 @@ function removeFromShake(id) for(var i = 0; i < shakingGuardList.length;i++) { if(shakingGuardList[i] == id) { shakingGuardList.splice(i, 1); //remove from list - //error(arguments.callee.name, "remove id =" + id + "(" + shakingGuardList + ")" ); + //error("remove id =" + id + "(" + shakingGuardList + ")" ); return; } } - error(arguments.callee.name, "design error id =" + id + "(" + shakingGuardList + ")" ); + error("design error id =" + id + "(" + shakingGuardList + ")" ); } //====================== diff --git a/lodeRunner.hiscore.js b/lodeRunner.hiscore.js index 8c97ba3..fb4f093 100644 --- a/lodeRunner.hiscore.js +++ b/lodeRunner.hiscore.js @@ -79,6 +79,16 @@ function showScoreTable(_playData, _curScoreInfo, _callbackFun, _waitTime) } + function setLastScoreInfo(scoreInfo) + { + if(typeof cId == "undefined") return; + + scoreInfo.id = cId; + var infoJSON = JSON.stringify(scoreInfo); + + setStorage(STORAGE_LASTSCORE+_playData, infoJSON); + } + function updateScoreInfo() { var addId = -1; @@ -246,7 +256,8 @@ function showScoreTable(_playData, _curScoreInfo, _callbackFun, _waitTime) //update name for score info hiScoreInfo[recordId].n = nameString; setHiScoreInfo(); - + setLastScoreInfo(hiScoreInfo[recordId]); //save last score + if(nameString != playerName && nameString != "???") { //set and save playerName playerName = nameString; setPlayerName(nameString); @@ -348,8 +359,10 @@ function showScoreTable(_playData, _curScoreInfo, _callbackFun, _waitTime) name[curPos] = nextChar(name[curPos], -1); break; case (code == KEYCODE_ENTER): //ENTER - if(name.length > 0) inputFinish(true); //async - else soundPlay("beep"); + if(vaildPlayerName(name)) { + restoreKeyDownHandler(); //avoid type "ENTER" twice + inputFinish(true); //async + } else soundPlay("beep"); break; default: //debug(code); @@ -420,7 +433,27 @@ function inputPlayerName(_stage, _callbackFun) _callbackFun(); } } - + +function vaildPlayerName(playerArray) +{ + var len = playerArray.length; + + //(1)skip tail space + for(var i = len-1; i >= 0; i--) { + if(playerArray[i] == " ") len--; + else break; + } + + //(2) string length must > 1 and don't all same char + if(len <= 1) return 0; //too short + + for(var i = 1; i < len; i++) { + if(playerArray[0] != playerArray[i]) return 1; //OK + } + + return 0; //all same char +} + function inputString(_stage, _maxSize, _startX, _startY, _defaultString, _callbackFun) { @@ -542,7 +575,7 @@ function inputString(_stage, _maxSize, _startX, _startY, _defaultString, _callba inputText[curPos] = nextChar(inputText[curPos], -1); break; case (code == KEYCODE_ENTER): //ENTER - if(inputText.length > 0) { + if(vaildPlayerName(inputText)) { inputFinish(); return true; } else soundPlay("beep"); diff --git a/lodeRunner.html b/lodeRunner.html index 2e553a8..fe9a017 100644 --- a/lodeRunner.html +++ b/lodeRunner.html @@ -18,7 +18,7 @@ * Source Code: https://github.com/SimonHung/LodeRunner_TotalRecall * Web page: http://LodeRunnerWebGame.com * - * by Simon Hung 06/20/2014, 11/23/2014, 06/29/2015, 09/28/2016 + * by Simon Hung 06/20/2014, 11/23/2014, 06/29/2015, 09/28/2016, 03/26/2025 * =================================================================================== --> @@ -63,6 +63,7 @@ +
diff --git a/lodeRunner.iconClass.js b/lodeRunner.iconClass.js index 2ac924a..79001eb 100644 --- a/lodeRunner.iconClass.js +++ b/lodeRunner.iconClass.js @@ -178,7 +178,9 @@ function selectIconClass( _screenX1, _screenY1, _scale, _bitmap) enabled = 1; disableMouseHandler(); enableMouseHandler(); - document.body.appendChild(selectCanvas); + + document.body.appendChild(selectCanvas); //overlay with selectCanvas, make zIndex up ? + selectIcon.set({alpha:1}) stage.enableMouseOver(60); stage.update(); @@ -369,6 +371,9 @@ function demoIconClass( _screenX1, _screenY1, _scale, _bitmap) enabled = 1; disableMouseHandler(); enableMouseHandler(); + + document.body.appendChild(canvas); //overlay with pasteCanvas, make zIndex up ? + iconObj.set({alpha:1}) stage.enableMouseOver(60); stage.update(); @@ -627,6 +632,8 @@ function soundIconClass( _screenX1, _screenY1, _scale, _soundOnBitmap, _soundOff if((soundOff ^= 1)) { soundStop(soundDig); soundStop(soundFall); } } + resumeAudioContext(); //2021/08/25 + self.updateSoundImage(); mouseOut(); } @@ -1240,6 +1247,132 @@ function themeColorIconUpdate() themeColorObj.themeChange(); } +// For EDIT mode +function pasteIconClass( _screenX1, _screenY1, _scale, _bitmap) +{ + var border = 4 * _scale; + var canvas, stage; + var iconObj, bgObj; + var saveStateObj; + var mouseOverHandler = null, mouseOutHandler = null, mouseClickHandler = null; + + var bitmapX = _bitmap.getBounds().width * _scale; + var bitmapY = _bitmap.getBounds().height * _scale; + var self = this; + var enabled = 0; + + init(); + + function init() + { + createCanvas(); + createIconObj(); + } + + this.enable = function () + { + if (enabled) return; + + enabled = 1; + disableMouseHandler(); + enableMouseHandler(); + + document.body.appendChild(canvas); //overlay with demoCanvas, make zIndex up ? + + iconObj.set({alpha:1}) + stage.enableMouseOver(60); + stage.update(); + } + + this.disable = function () + { + if (!enabled) return; + + disableMouseHandler(); + iconObj.set({alpha:0}); + stage.update(); + enabled = 0; + stage.enableMouseOver(0); + } + + function enableMouseHandler() + { + mouseOverHandler = iconObj.on("mouseover", mouseOver); + mouseOutHandler = iconObj.on("mouseout", mouseOut); + mouseClickHandler = iconObj.on("click", mouseClick); + } + + function disableMouseHandler() + { + iconObj.removeEventListener("mouseover", mouseOverHandler); + iconObj.removeEventListener("mouseout", mouseOutHandler); + iconObj.removeEventListener("click", mouseClickHandler); + stage.cursor = "default"; + stage.update(); + } + + function createCanvas() + { + canvas = document.createElement('canvas'); + canvas.id = "paste_icon"; + canvas.width = bitmapX+border*2; + canvas.height = bitmapY+border*2; + + var left = (_screenX1 - canvas.width - screenBorder), + top = (canvas.height*2 + bitmapY*3/2)|0; + + canvas.style.left = left + "px"; + canvas.style.top = top + "px"; + canvas.style.position = "absolute"; + document.body.appendChild(canvas); + } + + function createIconObj() + { + stage = new createjs.Stage(canvas); + iconObj = new createjs.Container(); + + //create mouseOver shape + bgObj = new createjs.Shape(); + bgObj.graphics.beginFill(mouseOverBGColor) + .drawRect(0, 0, bitmapX+border*2, bitmapY+border*2).endFill(); + bgObj.alpha = 0; + + //change bitmap size + _bitmap.setTransform(border, border, _scale, _scale); + + iconObj.addChild(bgObj); + iconObj.addChild(_bitmap); + iconObj.set({alpha:0}) + + stage.addChild(iconObj); + stage.update(); + } + + function mouseOver() + { + if(playMode != PLAY_EDIT) return; + + stage.cursor = "pointer"; + bgObj.alpha = 1; + stage.update(); + } + + function mouseOut() + { + stage.cursor = "default"; + bgObj.alpha = 0; + stage.update(); + } + + function mouseClick() + { + if(playMode != PLAY_EDIT) return; + mouseOut(); + editPasteMap(); + } +} + //====================================== // BEGIN: Change Theme Screen real time //====================================== diff --git a/lodeRunner.info.js b/lodeRunner.info.js index a333503..af534e5 100644 --- a/lodeRunner.info.js +++ b/lodeRunner.info.js @@ -126,7 +126,7 @@ function infoMenu(callbackFun, args) if(infoMsg) infoObj.showInfo(infoMsg, callbackFun, args); else { if(callbackFun) callbackFun(args); - error(arguments.callee.name, "Error: playMode = " + playMode); + error("Error: playMode = " + playMode); } } @@ -278,7 +278,7 @@ function infoMenuClass(_stage, _scale) menuY += (infoObj[i].textHeight * 3/2|0); break; default: - error(arguments.callee.name, "Error: type don't support, type = " + infoList[i].type); + error("Error: type don't support, type = " + infoList[i].type); continue; } if(maxTextWidth < infoObj[i].width) maxTextWidth = infoObj[i].width; @@ -373,7 +373,7 @@ function infoMenuClass(_stage, _scale) _stage.addChild(infoObj[i].textLink); break; default: - error(arguments.callee.name, "Error: type don't support, type = " + infoObj[i].type); + error("Error: type don't support, type = " + infoObj[i].type); continue; } } @@ -396,7 +396,7 @@ function infoMenuClass(_stage, _scale) _stage.removeChild(infoObj[i].textLink); break; default: - error(arguments.callee.name, "Error: type don't support, type = " + infoObj[i].type); + error("Error: type don't support, type = " + infoObj[i].type); continue; } } diff --git a/lodeRunner.key.js b/lodeRunner.key.js index 61aa07c..b37638e 100644 --- a/lodeRunner.key.js +++ b/lodeRunner.key.js @@ -268,6 +268,7 @@ function pressKey(code) menuIconDisable(1); gamePause(); showScoreTable(playData, null, function() { menuIconEnable(); gameResume();}); + return; //don't record this key code ! } else { keyAction = ACT_UNKNOWN; } @@ -277,7 +278,7 @@ function pressKey(code) //debug("keycode = " + code); break; } - if(recordMode && code != KEYCODE_ESC) saveKeyCode(code, keyAction); + if(recordMode && code != KEYCODE_ESC) saveKeyCode(code, keyAction); } function gameResume() diff --git a/lodeRunner.main.js b/lodeRunner.main.js index 24906ae..b5fa9d5 100644 --- a/lodeRunner.main.js +++ b/lodeRunner.main.js @@ -66,7 +66,7 @@ function init() var screenSize = getScreenSize(); screenX1 = screenSize.x; screenY1 = screenSize.y; - + canvasReSize(); createStage(); setBackground(); @@ -126,6 +126,7 @@ function canvasReSize() canvas.style.left = (left>0?left:0) + "px"; canvas.style.top = (top>0?top:0) + "px"; canvas.style.position = "absolute"; + canvas.style.cursor = "default"; tileW = BASE_TILE_X; //tileW and tileH for detection so don't change scale tileH = BASE_TILE_Y; @@ -505,7 +506,7 @@ function moveSprite2Top() } if(runner == null) { - error(arguments.callee.name, "Without runner ???"); + error("Without runner ???"); } else { //move runner to top (z index) moveChild2Top(mainStage, runner.sprite); @@ -1313,8 +1314,8 @@ function gameFinishCallback(selectMode) gameState = GAME_NEW_LEVEL; break; case 1: //menu selection - //incLevel(1); - activeSelectMenu(gameFinishActiveNew, gameFinishCloseIcon, null) + //incLevel(1); + activeSelectMenu(gameFinishActiveNew, gameFinishCloseIcon); break; case 2: //new level incLevel(1,0); @@ -1379,13 +1380,13 @@ function mainTick(event) } break; case PLAY_DEMO: - error(arguments.callee.name, "DEMO dead level=" + curLevel); + error("DEMO dead level=" + curLevel); setTimeout(function() {incLevel(1,0); gameState = GAME_NEW_LEVEL; }, 500); gameState = GAME_WAITING; break; case PLAY_DEMO_ONCE: - error(arguments.callee.name, "DEMO dead level=" + curLevel); + error("DEMO dead level=" + curLevel); disableStageClickEvent(); document.onkeydown = handleKeyDown; @@ -1442,7 +1443,7 @@ function mainTick(event) case PLAY_MODERN: soundPlay(soundEnding); var lastHiScore = lastHiScore = updateModernScoreInfo(); - levelPassDialog(curLevel, curGetGold, curGuardDeadNo, curTime, lastHiScore, + levelPassDialog(curLevel, curGetGold, curGuardDeadNo, curTime, lastHiScore, returnBitmap, select1Bitmap, nextBitmap, mainStage, tileScale, gameFinishCallback); gameState = GAME_WAITING; @@ -1453,7 +1454,7 @@ function mainTick(event) gameState = GAME_WAITING; break; default: - error(arguments.callee.name, "design error, playMode =" + playMode); + error("design error, playMode =" + playMode); break; } diff --git a/lodeRunner.menu.js b/lodeRunner.menu.js index 4aaac26..d630e83 100644 --- a/lodeRunner.menu.js +++ b/lodeRunner.menu.js @@ -314,16 +314,16 @@ function closeIconClass(_width, _height, _stage, _scale, _activeColor, _callBack // // // |< 3 >| |<4>|<4>| -// +--------------------------------------------------------+ -+- -// | 001 | 002 | 003 | | +// -+- +--------------------------------------------------------+ -+- +// SLIDE_GAP_Y | | 001 | 002 | 003 | | // -+- | +----------+ +----------+ +----------+ | // SELECT_SIZE_Y | | | | | | | | | | | // | | | | | | | | | // -+- | +----------+ | +----------+ | +----------+ | -// SLIDE_GAP_Y | | | +// SLIDE_GAP_Y1 | | | // -+- | - - - - - - - - | - - - - - - - - | - - - - - - - - | SLIDE_AREA_Y -// SLIDE_GAP_Y | | 004 005 006 | -// -+- | +----------+ | +----------+ | +----------+ | +// | 004 005 006 | +// | +----------+ | +----------+ | +----------+ | // | | | | | | | | // | | | | | | | | | | // | +----------+ +----------+ +----------+ | @@ -343,7 +343,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree var TITLE_TEXT_SIZE = 36 * _scale; var TABS_TEXT_SIZE = 20 * _scale; - var TOP_BORDER1 = TITLE_TEXT_SIZE * 3/2 | 0; + var TOP_BORDER1 = TITLE_TEXT_SIZE * 2 | 0; //new: *2, old: *3/2 var TOP_BORDER2 = TABS_TEXT_SIZE * 3/2 | 0; var BORDER1 = 20 * _scale; var BORDER2 = 16 * _scale; @@ -354,12 +354,14 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree var SELECT_SIZE_X = NO_OF_TILES_X*BASE_TILE_X*MAP_SCALE; var SELECT_SIZE_Y = NO_OF_TILES_Y*BASE_TILE_Y*MAP_SCALE; var SLIDE_PAGE_ITEMS = 3 * 10; - var SLIDE_GAP_X = BASE_TILE_X * _scale * 3/4; + var SLIDE_GAP_X = BASE_TILE_X * _scale * 3/5; //new: 3/5, old: 3/4 var SLIDE_GAP_Y = BASE_TILE_Y * _scale * 3/4; + var SLIDE_GAP_Y1 = BASE_TILE_Y * _scale * 2/4; //new: 2/4, old: 3/4 + var SLIDE_ITEM_X = 3; var SLIDE_ITEM_Y = 3; var SLIDE_AREA_X = (SLIDE_GAP_X*2+SELECT_SIZE_X)*SLIDE_ITEM_X; - var SLIDE_AREA_Y = (SLIDE_GAP_Y*2+SELECT_SIZE_Y)*SLIDE_ITEM_Y; + var SLIDE_AREA_Y = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y)*SLIDE_ITEM_Y; var CANVAS_SIZE_X = (BORDER1+BORDER2)*2 + SLIDE_AREA_X; var CANVAS_SIZE_Y = TOP_BORDER1 + TOP_BORDER2 + BORDER2 + SLIDE_AREA_Y +BORDER1+BORDER2; @@ -407,10 +409,13 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree var activeState = 0; //for edit mode only (1: active level shift, -1: active level deleted) var levelDeleted = 0; //for edit mode only + var saveKeyStateObj; init(); + function init() { + saveKeyState(); createCanvas1(); createDialogStage(); setBackground(); @@ -469,10 +474,38 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree document.body.appendChild(canvas1); } + function saveKeyState() + { + saveKeyStateObj = saveKeyHandler(handleEscKeyDown); + } + + function restoreKeyState() + { + restoreKeyHandler(saveKeyStateObj); + } + + function handleEscKeyDown(event) + { + if(!event){ event = window.event; } //cross browser issues exist + + if(event.shiftKey || event.ctrlKey) return false; + + switch(event.keyCode) { + case KEYCODE_ESC: + closeBox(); + break; + default: + //debug("keycode = " + code); + break; + } + return false; + } + function closeBox() { removeCanvas1(); removeCoverBackground(); + restoreKeyState(); if(_postFun) _postFun(); if(_closeFun) _closeFun(levelDeleted, _activeLevel, activeState); //for edit mode } @@ -481,6 +514,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree { dialogStage.removeAllChildren(); dialogStage.enableMouseOver(0); + dialogStage.cursor ="default"; createjs.Ticker.removeEventListener(dialogStage); document.body.removeChild(canvas1); } @@ -539,7 +573,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree .drawRect(BORDER1+BORDER2-1, CANVAS_SIZE_Y-BORDER1, SLIDE_AREA_X+2, BORDER1).endFill(); border1.addChild(vBorder1, vBorder2); - border1.on("mouseover", function() { dialogStage.cursor ="default" }); + border1.on("mouseover", function() { dialogStage.cursor ="default"; }); dialogStage.addChild(border1); } @@ -681,7 +715,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree var selectItemY = Math.ceil(pageItems/SLIDE_ITEM_X); var slideX = SLIDE_AREA_X; - var slideY = (SLIDE_GAP_Y*2+SELECT_SIZE_Y) * (selectItemY < SLIDE_ITEM_Y?SLIDE_ITEM_Y:selectItemY); + var slideY = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y) * (selectItemY < SLIDE_ITEM_Y?SLIDE_ITEM_Y:selectItemY); var slideStartX = BORDER1 + BORDER2; var slideStartY = TOP_BORDER1 + TOP_BORDER2 + BORDER2; @@ -759,7 +793,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree //--------------------- // move to block bound //--------------------- - var blockSizeY = (SLIDE_GAP_Y*2+SELECT_SIZE_Y); + var blockSizeY = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y); var startY = maxSliderY var mod = (maxSliderY - slider.y) % blockSizeY; //division remainder value to block bound @@ -775,7 +809,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree slider.x = slideStartX; slider.y = slideStartY; if(activeItemY > 0) { //scroll to active level - slider.y -= activeItemY*(SLIDE_GAP_Y*2+SELECT_SIZE_Y); + slider.y -= activeItemY*(SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y); } dialogStage.update(); @@ -799,12 +833,12 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree textColor); selectText[id].x = (SLIDE_GAP_X*2+SELECT_SIZE_X) * x + SLIDE_GAP_X; - selectText[id].y = (SLIDE_GAP_Y*2+SELECT_SIZE_Y) * y + SLIDE_GAP_Y - SELECT_TEXT_SIZE*4/3; + selectText[id].y = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y) * y + SLIDE_GAP_Y - SELECT_TEXT_SIZE*4/3; slider.addChild(selectText[id]); selectRect[id] = buildSelectMap(level, id); selectRect[id].x = (SLIDE_GAP_X*2+SELECT_SIZE_X) * x + SLIDE_GAP_X; - selectRect[id].y = (SLIDE_GAP_Y*2+SELECT_SIZE_Y) * y + SLIDE_GAP_Y; + selectRect[id].y = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y) * y + SLIDE_GAP_Y; slider.addChild(selectRect[id]); if(playMode != PLAY_DEMO || (playMode == PLAY_DEMO && (typeof demoData[level-1] != "undefined"))) { @@ -839,11 +873,11 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree selectText[id].text = ("00"+level).slice(-3); selectText[id].color = (_activeLevel == level)?SELECT_TEXT_ACTIVE_COLOR:SELECT_TEXT_COLOR; selectText[id].x = (SLIDE_GAP_X*2+SELECT_SIZE_X) * x + SLIDE_GAP_X; - selectText[id].y = (SLIDE_GAP_Y*2+SELECT_SIZE_Y) * y + SLIDE_GAP_Y - SELECT_TEXT_SIZE*4/3; + selectText[id].y = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y) * y + SLIDE_GAP_Y - SELECT_TEXT_SIZE*4/3; selectRect[id] = selectRect[id+1]; selectRect[id].x = (SLIDE_GAP_X*2+SELECT_SIZE_X) * x + SLIDE_GAP_X; - selectRect[id].y = (SLIDE_GAP_Y*2+SELECT_SIZE_Y) * y + SLIDE_GAP_Y; + selectRect[id].y = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y) * y + SLIDE_GAP_Y; selectRect[id].myLevel = level; selectRect[id].myId = level - startLevel; @@ -874,7 +908,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree pageItems = endLevel - startLevel+1; selectItemY = Math.ceil(pageItems/SLIDE_ITEM_X); - slideY = (SLIDE_GAP_Y*2+SELECT_SIZE_Y) * (selectItemY < SLIDE_ITEM_Y?SLIDE_ITEM_Y:selectItemY); + slideY = (SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y) * (selectItemY < SLIDE_ITEM_Y?SLIDE_ITEM_Y:selectItemY); minSliderY = slideStartY + SLIDE_AREA_Y - slideY; if(slider.y < minSliderY) slider.y = minSliderY; //shift slider } @@ -982,7 +1016,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree } } container.cache(0, 0, SELECT_SIZE_X, SELECT_SIZE_Y); - bitmap = new createjs.Bitmap( container.getCacheDataURL()); + bitmap = new createjs.Bitmap( container.cacheCanvas); //change "cont.getCacheDataURL()" to "cont.cacheCanvas" container.removeAllChildren(); return bitmap; } @@ -1043,6 +1077,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree debug("selectClick"); removeCanvas1(); removeCoverBackground(); + restoreKeyState(); if(_postFun) _postFun(); _activeFun(this.myLevel); } @@ -1080,7 +1115,7 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree direction = (direction>0)?1: -1; - slider.y += ((SLIDE_GAP_Y*2+SELECT_SIZE_Y)) * direction; + slider.y += ((SLIDE_GAP_Y+SLIDE_GAP_Y1+SELECT_SIZE_Y)) * direction; //boundLine1.alpha = boundLine2.alpha = 0; if(slider.y > maxSliderY) { slider.y = maxSliderY; @@ -1219,8 +1254,23 @@ function selectDialog(_titleName, _checkBitmap, _levelData, _activeLevel, _scree if(_activeLevel == level) { _activeLevel = 0; activeState = -1; //active level deleted + + clearTestLevel(); //test level deleted + } else if( _activeLevel > level) { - _activeLevel--; activeState = 1; //active level shift + _activeLevel--; + activeState = 1; //active level shift + + getTestLevel(testLevelInfo); + testLevelInfo.level = _activeLevel; // change level no + setTestLevel(testLevelInfo); + + } else if (_activeLevel == 0) { //0 meaings test level is new level + + if(testLevelInfo.modified == 1) { + testLevelInfo.level = editLevels+1; + setTestLevel(testLevelInfo); + } } //(3) shift or delete playInfo of user created @@ -1296,7 +1346,7 @@ function menuDialog(_titleName, _itemList, _stage, _scale, _closeIconEnable, _cl var ITEM_TEXT_COLOR = "white"; var ITEM_TEXT_SHADOW_COLOR = "gold"; - var TITLE_AREA_Y = TITLE_TEXT_SIZE * 2 | 0; + var TITLE_AREA_Y = TITLE_TEXT_SIZE * 2.5 | 0; var ITEM_AREA_Y = ITEM_TEXT_SIZE * 3/2 | 0; var ITEM_GAP_Y = ITEM_TEXT_SIZE * 2/3 | 0; @@ -1444,6 +1494,7 @@ function menuDialog(_titleName, _itemList, _stage, _scale, _closeIconEnable, _cl _itemList[0].activeItem = activeItemBackup; //recover menu active item restoreKeyHandler(saveKeyStateObj); removeAllObj(); + _stage.cursor = 'default'; _stage.enableMouseOver(0); if(_closeCallBack) _closeCallBack(_args); } @@ -1558,13 +1609,27 @@ var playVersionInfo = [ { id:2, verData: championData, name: gameVersionName[1], info: championInfo } ]; -var customItemInfo = { id:999, name:" Custom Levels " }; +//var customItemInfo = { id:999, name:" Custom Levels " }; +var customItemIdx = -1; var maxPlayId; var gameVersionMenuList = [ { activeItem: 0 } //game version menu ID ]; +function getCustomItemInfo(withLevel) +{ + var customItemName = " Custom Levels "; + if(withLevel) { + if (editLevels == 1) { + return customItemName + "(1 Level)"; + } else if(editLevels > 1) { + return customItemName + "(" + editLevels + " Levels)"; + } + } + return customItemName; +} + function initMenuVariable() { maxPlayId = 0; @@ -1578,7 +1643,9 @@ function initMenuVariable() ); if(maxPlayId < playVersionInfo[i].id) maxPlayId = playVersionInfo[i].id; } - gameVersionMenuList.push({name: customItemInfo.name, activeFun: subEditMenu}); + var customItemName = getCustomItemInfo(1); + gameVersionMenuList.push({name: customItemName, activeFun: subEditMenu}); + customItemIdx = gameVersionMenuList.length-1; } function getPlayVerData(id) @@ -1587,7 +1654,7 @@ function getPlayVerData(id) if(playVersionInfo[i].id == id) return playVersionInfo[i].verData; } - error(arguments.callee.name, "Error: versionData can not find, id = " + id ); + error("Error: versionData can not find, id = " + id ); return playVersionInfo[0].verData; } @@ -1597,7 +1664,7 @@ function getPlayVerInfo(id) if(playVersionInfo[i].id == id) return playVersionInfo[i].info; } - error(arguments.callee.name, "Error: version info can not find, id = " + id ); + error("Error: version info can not find, id = " + id ); return playVersionInfo[0].info; } @@ -1611,7 +1678,7 @@ function menuIdToPlayData(menuId) if(menuId == playVersionInfo.length) return PLAY_DATA_USERDEF; //user created else if (menuId < playVersionInfo.length) return playVersionInfo[menuId].id; - error(arguments.callee.name, "design error, menuId =" + menuId ); + error("design error, menuId =" + menuId ); return playVersionInfo[0].id; } @@ -1625,17 +1692,19 @@ function playDataToTitleName(verId) if(playVersionInfo[i].id == verId) return playVersionInfo[i].name; } - error(arguments.callee.name, "design error, id =" + verId ); + error("design error, id =" + verId ); return "Unknown"; } function mainMenu(callbackFun) { + gameVersionMenuList[customItemIdx].name = getCustomItemInfo(1); menuDialog(" Select Game Version ", gameVersionMenuList, mainStage, tileScale, 0, mainMenuClose, callbackFun); } function gameVersionMenu(id, callbackFun) { + gameVersionMenuList[customItemIdx].name = getCustomItemInfo(1); menuDialog(" Select Game Version ", gameVersionMenuList, mainStage, tileScale, 1, mainMenuClose, callbackFun); } @@ -1654,7 +1723,7 @@ function playData2GameVersionMenuId() } } - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); gameVersionMenuList[0].activeItem = 0; return 0; } @@ -1675,8 +1744,10 @@ function gameMenu(callbackFun) { var titleName; - var demoItemObj = { name: " Demo Mode ", activeFun: demoPlay }; - var editPlayItemObj = { name: " Play Mode ", activeFun: editPlay }; + var demoItemObj = { name: " Demo Mode ", activeFun: demoPlay }; + var editPlayItemObj = { name: " Play Mode ", activeFun: editPlay }; + var editBackupItemObj = { name: " Backup ", activeFun: backupDialog }; + var editRestoreItemObj = { name: " Restore ", activeFun: restoreDialog }; var gameMenuList = [ { activeItem: 0 }, @@ -1687,18 +1758,26 @@ function gameMenu(callbackFun) ]; var editMenuList = [ - { activeItem: 0 }, - { name: " Edit Mode", activeFun: editEdit }, -// { name: " Play ", activeFun: editPlay }, - { name: " Change Game Version ", activeFun: gameVersionMenu } + { activeItem: 0 }, //[0] + { name: " Edit Mode", activeFun: editEdit }, //[1] +// { name: " Play ", activeFun: editPlay }, + { name: " Change Game Version ", activeFun: gameVersionMenu } //[2] ]; switch(true) { case (gameVersionMenuList[0].activeItem == playVersionInfo.length): - titleName = customItemInfo.name; - if(playMode == PLAY_EDIT || playMode == PLAY_TEST) editMenuList[0].activeItem = 0; //edit.edit - else editMenuList[0].activeItem = 1; // edit.play - if(editLevels > 0) addMenuItem(editMenuList, editPlayItemObj, 2); + titleName = getCustomItemInfo(0); + if(playMode == PLAY_EDIT || playMode == PLAY_TEST) { + editMenuList[0].activeItem = 0; //edit.edit + } else + editMenuList[0].activeItem = 1; // edit.play + + addMenuItem(editMenuList, editRestoreItemObj, 2); + + if(editLevels > 0) { + addMenuItem(editMenuList, editBackupItemObj, 2); + addMenuItem(editMenuList, editPlayItemObj, 2); + } menuDialog(titleName, editMenuList, mainStage, tileScale, 1, mainMenuClose, callbackFun); return; case (gameVersionMenuList[0].activeItem < playVersionInfo.length): @@ -1706,7 +1785,7 @@ function gameMenu(callbackFun) if(playerDemoData.length > 0) addMenuItem(gameMenuList, demoItemObj, 3); break default: - error(arguments.callee.name, "design error, value =" + gameVersionMenuList[0].activeItem ); + error("design error, value =" + gameVersionMenuList[0].activeItem ); return; } @@ -1763,6 +1842,7 @@ function classicPlay(id, callbackFun) setLastPlayMode(); selectIconObj.disable(1); demoIconObj.disable(1); + pasteIconObj.disable(); initShowDataMsg(); startGame(); } @@ -1777,6 +1857,7 @@ function modernPlay(id, callbackFun) soundStop(soundFall); disableStageClickEvent(); document.onkeydown = handleKeyDown; + pasteIconObj.disable(); setLastPlayMode(); initShowDataMsg(); startGame(); @@ -1795,6 +1876,7 @@ function demoPlay(id, callbackFun) anyKeyStopDemo(); initShowDataMsg(); demoIconObj.disable(1); + pasteIconObj.disable(); startGame(); } @@ -1808,10 +1890,17 @@ function editPlay(id, callbackFun) disableStageClickEvent(); document.onkeydown = handleKeyDown; setLastPlayMode(); - selectIconObj.disable(1); + //selectIconObj.disable(1); demoIconObj.disable(1); - initShowDataMsg(); - startGame(); + pasteIconObj.disable(); + if (id < 0) { //id < 0 ==> means call from restore custom levels + initShowDataMsg(0); //no tips message + setTimeout(function() { showTipsText("RESTORE COMPLETE", 2500);}, 50); + startGame(1); // no cycle + } else { + initShowDataMsg(); + startGame(); + } } function editEdit(id, callbackFun) @@ -1819,7 +1908,13 @@ function editEdit(id, callbackFun) if(callbackFun != null) callbackFun(); disableStageClickEvent(); demoIconObj.disable(1); - initShowDataMsg(); + pasteIconObj.disable(); + if (id < 0) { //id < 0 ==> means call from restore custom levels + initShowDataMsg(0); + setTimeout(function() { showTipsText("RESTORE COMPLETE", 2500);}, 50); + } else { + initShowDataMsg(); + } startEditMode(); } @@ -1853,8 +1948,9 @@ function activeSelectMenu(activeFun, postFun) } } +// if _level < 0 means "share level" pass dialog function levelPassDialog(_level, _getGold, _guardDead, _time, _hiScore, - _returnBitmap, _menuBitmap, _nextBitmap, + _button1Bitmap, _button2Bitmap, _button3Bitmap, _stage, _scale, _callBack) { var TITLE_TEXT_SIZE = 48 * _scale; @@ -1884,8 +1980,8 @@ function levelPassDialog(_level, _getGold, _guardDead, _time, _hiScore, var BUTTON_COLOR = "#eeffff" var BUTTON_BACKGROUND_COLOR = BACKGROUND_COLOR; - var buttonX = _returnBitmap.getBounds().width * _scale, - buttonY = _returnBitmap.getBounds().height * _scale; + var buttonX = _button1Bitmap.getBounds().width * _scale, + buttonY = _button1Bitmap.getBounds().height * _scale; var titleTextObj, goldTextObj, guardTextObj, timeTextObj; var goldObj, guardObj, timeNameObj; @@ -1899,7 +1995,7 @@ function levelPassDialog(_level, _getGold, _guardDead, _time, _hiScore, var menuX, menuY, startX, startY var timeNameX, maxTextSize; var menuButton = []; - var bitmap = [ _returnBitmap, _menuBitmap, _nextBitmap ]; + var bitmap = [ _button1Bitmap, _button2Bitmap, _button3Bitmap ]; var centerX, xOffset, yOffset; @@ -1983,7 +2079,7 @@ function levelPassDialog(_level, _getGold, _guardDead, _time, _hiScore, xOffset = centerX; yOffset = startY+TITLE_TEXT_SIZE*3; - goldObj = drawText(xOffset, yOffset, "@", _stage); //@: gold + goldObj = drawText(xOffset, yOffset-TEXT_GAP_Y/3, "@", _stage); //@: gold var xOffset = centerX - GOLD_X - centerGap; goldObj[0].x = xOffset; goldObj[0].alpha = 0; @@ -1994,7 +2090,7 @@ function levelPassDialog(_level, _getGold, _guardDead, _time, _hiScore, _stage.addChild(goldTextObj); yOffset += (ITEM_TEXT_SIZE + TEXT_GAP_Y); - guardObj = drawText(xOffset, yOffset, "#", _stage); //#: trap (陷井) + guardObj = drawText(xOffset, yOffset-TEXT_GAP_Y/3, "#", _stage); //#: trap (陷井) guardObj[0].alpha = 0; guardTextObj.x =centerX + centerGap; @@ -2029,7 +2125,7 @@ function levelPassDialog(_level, _getGold, _guardDead, _time, _hiScore, hiScoreTextObj.y = yOffset; _stage.addChild(hiScoreNameObj, hiScoreTextObj); - yOffset += (ITEM_TEXT_SIZE + buttonY); //xOffset for menu button + yOffset += (ITEM_TEXT_SIZE + buttonY); //yOffset for menu button } function updateHiScore() @@ -2306,7 +2402,8 @@ function yesNoDialog(_txtMsg, _yesBitmap, _noBitmap, _stage, _scale, _callBack) textObj = []; for(var i = 0; i < _txtMsg.length; i++) { textObj[i] = new createjs.Text(_txtMsg[i], "bold " + TEXT_MSG_SIZE + "px Helvetica",TEXT_MSG_COLOR); - textObj[i].shadow = new createjs.Shadow(TEXT_MSG_SHADOW_COLOR, 0, 0, 10 ); + if( i == 0 ) //only first line message has shadow + textObj[i].shadow = new createjs.Shadow(TEXT_MSG_SHADOW_COLOR, 0, 0, 10 ); tmpTextSize = textObj[i].getBounds().width; if(maxTextSize < tmpTextSize) maxTextSize = tmpTextSize; } @@ -2437,3 +2534,1238 @@ function yesNoDialog(_txtMsg, _yesBitmap, _noBitmap, _stage, _scale, _callBack) _stage.update(); } } + + +function backupDialog(id, _callBackFun) +{ + var _stage = mainStage; + var _scale = tileScale; + + var TITLE_TEXT_SIZE = 48 * _scale; + var MSG_TEXT_SIZE = 40 * _scale; + var BUTTON_TEXT_SIZE = 40 * _scale; + + var BUTTON_BORDER_SIZE = 8 * _scale; + var BUTTON_ROUND_RADIUD = 2+ 4 * _scale; + + var COVER_BACKGROUND_COLOR = "black"; + + var BUTTON_BACKGROUND_BORDER_COLOR = "white"; + var BUTTON_BACKGROUND_SHADOW = "gold"; + var BUTTON_BACKGROUND_COLOR = "#5b0680"; //"#890ee0"; + var BUTTON_BACKGROUND_BORDER_SIZE = 8 * _scale;; + + var TITLE_TEXT_COLOR = "white"; + var TITLE_TEXT_SHADOW_COLOR = "gold"; + + var MSG_TEXT_COLOR = "white"; + + var BUTTON_TEXT_COLOR = "white" + var BUTTON_TEXT_SHADOW_COLOR = "gold"; + + var BUTTON_BORDER0_COLOR = "white"; //"#890ee0"; + var BUTTON_COLOR = "#5d7cff"; + var BUTTON_BORDER1_COLOR = "gold"; + + var CLOSE_ICON_ACTIVE_COLOR = "#ff5050"; + + var MSG_AREA_Y = MSG_TEXT_SIZE * 3/2 | 0; + var MSG_GAP_Y = MSG_TEXT_SIZE * 2/3 | 0; + + var coverBackgroundObj; + var background1Obj, background2Obj; + var titleTextObj, msgTextObj; + + var buttonTextObj, buttonObj; + + var closeIconObj; + var saveKeyStateObj; + + var buttonSizeX; + var menuX, menuY; + var startX, startY; + + var screenX1 = _stage.canvas.width; + var screenY1 = _stage.canvas.height; + + init(); + + function init() + { + saveKeyState(); + coverParentStage(); + initContent(); + creatBackground(); + createContent(); + closeIconObj = new closeIconClass(startX+menuX, startY, _stage, _scale, CLOSE_ICON_ACTIVE_COLOR, closeDialog, null); + + _stage.enableMouseOver(120); + _stage.update(); + } + + function saveKeyState() + { + saveKeyStateObj = saveKeyHandler(handleEscKeyDown); + } + + function restoreKeyState() + { + restoreKeyHandler(saveKeyStateObj); + } + + function coverParentStage() + { + coverBackgroundObj = new createjs.Shape(); + coverBackgroundObj.graphics.beginFill(COVER_BACKGROUND_COLOR).drawRect(0, 0, screenX1, screenY1).endFill(); + coverBackgroundObj.alpha = 0.6; + _stage.addChild(coverBackgroundObj); + } + + function initContent() + { + titleTextObj = new createjs.Text(" Backup ", "bold "+TITLE_TEXT_SIZE + "px Helvetica",TITLE_TEXT_COLOR); + titleTextObj.shadow = new createjs.Shadow(TITLE_TEXT_SHADOW_COLOR, 0, 0, 10 ); + + msgTextObj = new createjs.Text("Back up all custom levels to a file", "bold " + MSG_TEXT_SIZE + "px Helvetica", MSG_TEXT_COLOR); + + buttonTextObj = new createjs.Text("Backup", "bold " + BUTTON_TEXT_SIZE + "px Helvetica", BUTTON_TEXT_COLOR); + + buttonSizeX = buttonTextObj.getBounds().width + BUTTON_TEXT_SIZE; + menuX = msgTextObj.getBounds().width + TITLE_TEXT_SIZE * 2; + menuY = TITLE_TEXT_SIZE * 2.5 + (MSG_TEXT_SIZE *2) + MSG_GAP_Y * 4|0; + + startX = (screenX1-menuX)/2|0; + startY = (screenY1-menuY)/2|0; + } + + function creatBackground() + { + background1Obj = new createjs.Shape(); + background1Obj.graphics.beginFill(BUTTON_BACKGROUND_BORDER_COLOR) + .drawRoundRect(startX, startY, menuX, menuY, 8*_scale).endFill(); + background1Obj.shadow = new createjs.Shadow(BUTTON_BACKGROUND_SHADOW, 3, 3, 5 ); + + background2Obj = new createjs.Shape(); + background2Obj.graphics.beginFill(BUTTON_BACKGROUND_COLOR) + .drawRoundRect(startX+BUTTON_BACKGROUND_BORDER_SIZE, startY+BUTTON_BACKGROUND_BORDER_SIZE, + menuX-BUTTON_BACKGROUND_BORDER_SIZE*2, menuY-BUTTON_BACKGROUND_BORDER_SIZE*2, 8*_scale).endFill(); + _stage.addChild(background1Obj, background2Obj); + } + + function createContent() + { + titleTextObj.x = startX + (menuX - titleTextObj.getBounds().width)/2|0; + titleTextObj.y = startY + TITLE_TEXT_SIZE*3/4; + _stage.addChild(titleTextObj); + + var yOffset = (TITLE_TEXT_SIZE*2.2)|0; + + msgTextObj.x = startX + (menuX - msgTextObj.getBounds().width)/2|0; + msgTextObj.y = startY + yOffset; + _stage.addChild(msgTextObj); + + buttonObj = new createjs.Container(); + + //child id = 0 + var border = new createjs.Shape(); + border.graphics.beginFill(BUTTON_BORDER0_COLOR) + .drawRoundRect(-BUTTON_BORDER_SIZE/2, -BUTTON_BORDER_SIZE/2, buttonSizeX+BUTTON_BORDER_SIZE, MSG_AREA_Y+BUTTON_BORDER_SIZE ,BUTTON_ROUND_RADIUD).endFill(); + + //child id = 1 + var button = new createjs.Shape(); + button.graphics.beginFill(BUTTON_COLOR).drawRoundRect(0, 0, buttonSizeX, MSG_AREA_Y,BUTTON_ROUND_RADIUD).endFill(); + + //child id = 2 + buttonTextObj.textAlign = "center"; + buttonTextObj.x = buttonSizeX/2|0; + buttonTextObj.y = (MSG_AREA_Y - msgTextObj.getBounds().height)/2|0; + + yOffset += (MSG_GAP_Y*2 + TITLE_TEXT_SIZE/2)|0; + buttonObj.addChild(border, button, buttonTextObj); + buttonObj.x = startX + (menuX - buttonSizeX)/2|0; + buttonObj.y = startY+ yOffset; + + buttonObj.on('click', buttonClick); + buttonObj.on('mouseover', buttonMouseOver); + buttonObj.on('mouseout', buttonMouseOut); + _stage.addChild(buttonObj); + } + + function buttonClick() + { + var downloadfileName = downloadCustomLevelData(); + restoreKeyState(); + removeAllObj(); + _stage.cursor = 'default'; + _stage.enableMouseOver(0); + if(_callBackFun) _callBackFun(); + + setTimeout(function() { showTipsText("BACKUP IS COMPLETE", 2500);}, 50); + } + + function removeAllObj() + { + _stage.removeChild(coverBackgroundObj, background1Obj, background2Obj); + _stage.removeChild(titleTextObj, msgTextObj, buttonObj); + _stage.removeChild(closeIconObj); + _stage.update(); + } + + function buttonMouseOver() + { + var border = buttonObj.getChildAt(0); + + border.graphics.clear(); + border.graphics.beginFill(BUTTON_BORDER1_COLOR) + .drawRoundRect(-BUTTON_BORDER_SIZE, -BUTTON_BORDER_SIZE, buttonSizeX+BUTTON_BORDER_SIZE*2, MSG_AREA_Y+BUTTON_BORDER_SIZE*2,BUTTON_ROUND_RADIUD).endFill(); + + buttonTextObj.shadow = new createjs.Shadow(TITLE_TEXT_SHADOW_COLOR, 2, 2, 10 ); + _stage.cursor = 'pointer'; + _stage.update(); + } + + function buttonMouseOut() + { + var border = buttonObj.getChildAt(0); + + border.graphics.clear(); + border.graphics.beginFill(BUTTON_BORDER0_COLOR) + .drawRoundRect(-BUTTON_BORDER_SIZE/2, -BUTTON_BORDER_SIZE/2, buttonSizeX+BUTTON_BORDER_SIZE, MSG_AREA_Y+BUTTON_BORDER_SIZE, BUTTON_ROUND_RADIUD).endFill(); + + buttonTextObj.shadow = null; + _stage.cursor = 'default'; + _stage.update(); + } + + function closeDialog() + { + restoreKeyState(); + removeAllObj(); + _stage.cursor = 'default'; + _stage.enableMouseOver(0); + if(_callBackFun) _callBackFun(); + } + + function getBackupFileName(d) + { + //var d = new Date(); + + //LR-05.10.21.141804.lrwg + return 'LR'+ ('0'+(d.getMonth()+1)).slice(-2) + ('0'+d.getDate()).slice(-2) + ('0'+d.getFullYear()).slice(-2) + '-'+ ('0'+d.getHours()).slice(-2) + ('0'+d.getMinutes()).slice(-2) + ('0'+d.getSeconds()).slice(-2) + '-' + ('00'+editLevels).slice(-3) + '.lrwg'; + } + + function downloadCustomLevelData() + { + var date = new Date(); + var fileName = getBackupFileName(date); + + download(fileName, backupCustomLevelData(date)); + + return fileName; + + function download(filename, text) { + //reference: https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server + var element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', filename); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); + } + + } + + function handleEscKeyDown(event) + { + if(!event){ event = window.event; } //cross browser issues exist + + if(event.shiftKey || event.ctrlKey) return false; + + switch(event.keyCode) { + case KEYCODE_ESC: + closeDialog(); + break; + default: + //debug("keycode = " + code); + break; + } + return false; + } + +} + +function restoreDialog(id, _callBackFun) +{ + //LODE RUNNER WEB GAME.Wed May 12 2021 19:04:16 GMT+0800 (台北標準時間).PLAYER.XXXXX\n + + var _scale = tileScale; + + var MAX_DISPLAY_FILENAME_LEN = 26; + + var TITLE_TEXT_SIZE = 48 * _scale; + var MSG_TEXT_SIZE = 40 * _scale; + var BUTTON_TEXT_SIZE = 40 * _scale; + var FILEINFO_TEXT_SIZE = 36 * _scale; + var MAPNO_TEXT_SIZE = 20 * _scale; + var TOTALLEVEL_TEXT_SIZE = 28 * _scale; + + var BUTTON_BORDER_SIZE = 8 * _scale; + var BUTTON_ROUND_RADIUD = 2+ 4 * _scale; + + var COVER_BACKGROUND_COLOR = "black"; + + var BUTTON_BACKGROUND_BORDER_COLOR = "white"; + var BUTTON_BACKGROUND_SHADOW = "gold"; + var BUTTON_BACKGROUND_COLOR = "#5b0680"; //"#890ee0"; + var BUTTON_BACKGROUND_BORDER_SIZE = 8 * _scale; + + var PREVIEWMAP_BUTTON_COLOR = "white"; + var PREVIEWMAP_BUTTON_BORDER_SIZE = 4 * _scale; + var PREVIEWMAP_BUTTON_GRAY_ALPHA = 0.2; + + var TITLE_TEXT_COLOR = "white"; + var TITLE_TEXT_SHADOW_COLOR = "gold"; + + var MSG_TEXT_COLOR = "white"; + + var WRONG_TEXT_COLOR = "#f54B42"; + var WRONG_TEXT_OUTLINE_COLOR = "#FFFFB0"; + var WRONG_TEXT_SHADOW_COLOR = "#404040"; + + var BUTTON_TEXT_COLOR = "white" + var BUTTON_TEXT_SHADOW_COLOR = "gold"; + + var BUTTON_BORDER0_COLOR = "white"; + var BUTTON_COLOR = "#5d7cff"; + var BUTTON_BORDER1_COLOR = "gold"; + + var OPENBUTTON_BACKGROUND_COLOR = BUTTON_BACKGROUND_COLOR, + OPENBUTTON_COLOR = "#FFFFFF", + OPENBUTTON_BORDER_COLOR = "gold"; + + var OPENBUTTON_BORDER_SIZE = 8 * _scale, + OPENBUTTON_ROUND_RADIUD = 2+ 4 * _scale; + + var FILEINFO_TEXT_COLOR = "#EEEEEE"; + + var CLOSE_ICON_ACTIVE_COLOR = "#ff5050"; + + var MSG_AREA_Y = MSG_TEXT_SIZE * 3/2 | 0; + var MSG_GAP_Y = MSG_TEXT_SIZE * 2/3 | 0; + + var PREVIEW_MAP_SCALE = tileScale * 0.13; + var previewMapX = NO_OF_TILES_X*BASE_TILE_X*PREVIEW_MAP_SCALE, + previewMapY = NO_OF_TILES_Y*BASE_TILE_Y*PREVIEW_MAP_SCALE; + var previewMapGapX = 16*tileScale, + previewMapPerLine = 5; + + var coverBackgroundObj; + + var restoreCanvas; + var dialogStage; + + var background1Obj, background2Obj; + var titleTextObj, msgTextObj; + var fileTextObj, fileNameTextObj, fileNameBackgroundObj; + var openFolderButtonObj; + var wrongText1Obj, wrongText2Obj; + var fileInfoTextObj; + var buttonTextObj, buttonObj; + var fileNameObj = null; + + var closeIconObj; + var saveKeyStateObj; + + var maxFileNameSize, totalFileNameSizeX, totalPreviewMapSizeX; + var previewNoYOffset, previewMapYOffset; + + var buttonSizeX; + var menuX, menuY; + var fileNameX, fileNameY; + var wrongMsgEnable = 0; + + var openFolderButtonScale = 0.9; + var openFolderButtonX = openFolderBitmap.getBounds().width * tileScale * openFolderButtonScale, + openFolderButtonY = openFolderBitmap.getBounds().height * tileScale * openFolderButtonScale; + + var nextButtonX = nextBitmap.getBounds().width * tileScale, + nextButtonY = nextBitmap.getBounds().height * tileScale; + + var inputFileElement = null; + + var tmpCustomLevels; + var levelMapNoObj = levelMapObjs = []; + var totalLevelTextObj = null; + var nextMapButtonObj, prevMapButtonObj; + var previewvMapFirst = previewMapLast = -1; + + init(); + + function init() + { + saveKeyState(); + coverParentStage(); + createInputFileElement(); + initContent(); + createCanvas(); + createDialogStage(); + + creatBackground(); + createContent(); + closeIconObj = new closeIconClass(menuX, 0, dialogStage, _scale, CLOSE_ICON_ACTIVE_COLOR, closeDialog, null); + } + + function saveKeyState() + { + saveKeyStateObj = saveKeyHandler(handleEscKeyDown); + } + + function restoreKeyState() + { + restoreKeyHandler(saveKeyStateObj); + } + + function coverParentStage() + { + coverBackgroundObj = new createjs.Shape(); + coverBackgroundObj.graphics.beginFill(COVER_BACKGROUND_COLOR).drawRect(0, 0, mainStage.canvas.width, mainStage.canvas.height).endFill(); + coverBackgroundObj.alpha = 0.6; + mainStage.addChild(coverBackgroundObj); // mainStage + mainStage.update(); + } + + function createInputFileElement() + { + inputFileElement = document.createElement('input'); + inputFileElement.type = 'file'; + inputFileElement.accept = '.lrwg'; + inputFileElement.style.display = 'none'; + document.body.appendChild(inputFileElement); + inputFileElement.addEventListener('change', readRestoreFile); + } + + function initContent() + { + titleTextObj = new createjs.Text("Restore", "bold "+TITLE_TEXT_SIZE + "px Helvetica",TITLE_TEXT_COLOR); + titleTextObj.shadow = new createjs.Shadow(TITLE_TEXT_SHADOW_COLOR, 0, 0, 10 ); + + msgTextObj = new createjs.Text("Restore custom levels from backup files", "bold " + MSG_TEXT_SIZE + "px Helvetica", MSG_TEXT_COLOR); + + fileTextObj = new createjs.Text("File:", "bold " + MSG_TEXT_SIZE + "px Helvetica", MSG_TEXT_COLOR); + + wrongText1Obj = new createjs.Text("Wrong file format !", "bold " + MSG_TEXT_SIZE + "px Helvetica", WRONG_TEXT_OUTLINE_COLOR); + wrongText1Obj.outline = _scale; + wrongText2Obj = new createjs.Text("Wrong file format !", "bold " + MSG_TEXT_SIZE + "px Helvetica", WRONG_TEXT_COLOR); + wrongText2Obj.shadow = new createjs.Shadow(WRONG_TEXT_SHADOW_COLOR, _scale, _scale*2, 1); + + //get maximun file name text size (width) + var tmpObj = new createjs.Text("X", "bold "+ MSG_TEXT_SIZE + "px Courier New", MSG_TEXT_COLOR); + maxFileNameSize = tmpObj.getBounds().width * MAX_DISPLAY_FILENAME_LEN; + + fileInfoTextObj = new createjs.Text("Preview","bold " + FILEINFO_TEXT_SIZE + "px Helvetica", FILEINFO_TEXT_COLOR); + + totalFileNameSizeX = fileTextObj.getBounds().width + maxFileNameSize + (tileWScale*2/3) + openFolderButtonX; + totalPreviewMapSizeX = (previewMapX+previewMapGapX) * previewMapPerLine + previewMapGapX; + + menuX = TITLE_TEXT_SIZE * 2 + (totalFileNameSizeX > totalPreviewMapSizeX?totalFileNameSizeX:totalPreviewMapSizeX) |0; + menuY = (TITLE_TEXT_SIZE * 2.5 + (MSG_TEXT_SIZE+MSG_GAP_Y)*2) + ((MSG_TEXT_SIZE+MSG_GAP_Y)*2+ previewMapY + TITLE_TEXT_SIZE) + (MSG_GAP_Y*3 + BUTTON_TEXT_SIZE) |0; + + buttonTextObj = new createjs.Text(" Restore ", "bold " + BUTTON_TEXT_SIZE + "px Helvetica", BUTTON_TEXT_COLOR); + + buttonSizeX = buttonTextObj.getBounds().width + BUTTON_TEXT_SIZE; + } + + function createCanvas() + { + restoreCanvas = document.createElement('canvas'); + restoreCanvas.id = "restoreDialog"; + restoreCanvas.width = menuX+10; //10 px for shadow + restoreCanvas.height = menuY+10; //10 px for shadow + + var left = (screenX1 - restoreCanvas.width)/2|0, + top = (screenY1 - restoreCanvas.height)/2|0; + + restoreCanvas.style.left = left + "px"; + restoreCanvas.style.top = top + "px"; + restoreCanvas.style.position = "absolute"; + document.body.appendChild(restoreCanvas); + } + + function createDialogStage() + { + dialogStage = new createjs.Stage(restoreCanvas); + dialogStage.enableMouseOver(60); + //createjs.Ticker.setFPS(20); + createjs.Ticker.addEventListener("tick", dialogStage); + } + + function creatBackground() + { + background1Obj = new createjs.Shape(); + background1Obj.graphics.beginFill(BUTTON_BACKGROUND_BORDER_COLOR) + .drawRoundRect(0, 0, menuX, menuY, 8*_scale).endFill(); + background1Obj.shadow = new createjs.Shadow(BUTTON_BACKGROUND_SHADOW, 3, 3, 5 ); + + background2Obj = new createjs.Shape(); + background2Obj.graphics.beginFill(BUTTON_BACKGROUND_COLOR) + .drawRoundRect(BUTTON_BACKGROUND_BORDER_SIZE, BUTTON_BACKGROUND_BORDER_SIZE, + menuX-BUTTON_BACKGROUND_BORDER_SIZE*2, menuY-BUTTON_BACKGROUND_BORDER_SIZE*2, 8*_scale).endFill(); + dialogStage.addChild(background1Obj, background2Obj); + } + + function createContent() + { + titleTextObj.x = (menuX - titleTextObj.getBounds().width)/2|0; + titleTextObj.y = TITLE_TEXT_SIZE*3/4|0; + dialogStage.addChild(titleTextObj); + + var yOffset = TITLE_TEXT_SIZE*2.2|0; + + msgTextObj.x = (menuX - msgTextObj.getBounds().width)/2|0; + msgTextObj.y = yOffset; + dialogStage.addChild(msgTextObj); + + yOffset += (MSG_TEXT_SIZE+MSG_GAP_Y)|0; + fileTextObj.x = (menuX - totalFileNameSizeX)/2|0; + fileTextObj.y = yOffset; + dialogStage.addChild(fileTextObj); + + fileNameX = fileTextObj.x + fileTextObj.getBounds().width + (tileWScale/3)|0; + fileNameY = yOffset; + + fileNameBackgroundObj = new createjs.Shape(); + fileNameBackgroundObj.graphics.beginFill("white").drawRect( + fileNameX, fileNameY, maxFileNameSize, MSG_TEXT_SIZE).endFill(); + fileNameBackgroundObj.alpha = 0.2; + + dialogStage.addChild(fileNameBackgroundObj); + + addOpenButton(fileNameX+maxFileNameSize+(tileWScale*1/3)|0, fileNameY+(MSG_TEXT_SIZE-openFolderButtonY)/2|0); + + yOffset += (MSG_TEXT_SIZE+MSG_GAP_Y)|0; // fileInfo Box yOffset + + var fileInfoBackgroundObj = new createjs.Shape(); + var fileInfoTextBarObj = new createjs.Shape(); + var fileInfoBoxSizeY = (MSG_TEXT_SIZE+MSG_GAP_Y)*2 + previewMapY + MSG_TEXT_SIZE | 0; + var fileInfoBoxYOffset = yOffset; + + fileInfoBackgroundObj.graphics.beginFill("white").drawRoundRect(TITLE_TEXT_SIZE, yOffset, menuX-TITLE_TEXT_SIZE*2, fileInfoBoxSizeY, 16*_scale).endFill(); + fileInfoBackgroundObj.alpha = 0.5; + dialogStage.addChild(fileInfoBackgroundObj); + + fileInfoTextObj.x = (menuX-fileInfoTextObj.getBounds().width)/2; + fileInfoTextObj.y = yOffset+ (MSG_GAP_Y/2)|0; // prevew text yOffset + dialogStage.addChild(fileInfoTextObj); + + previewNoYOffset = fileInfoTextObj.y + (MSG_TEXT_SIZE+MSG_GAP_Y/2)|0; + previewMapYOffset = previewNoYOffset + MSG_GAP_Y; + + //set Wrong file format x,y + wrongText1Obj.x = wrongText2Obj.x = (menuX-wrongText1Obj.getBounds().width)/2|0; + wrongText1Obj.y = wrongText2Obj.y = fileInfoBoxYOffset + (fileInfoBoxSizeY- wrongText1Obj.getBounds().height)/2; //Y center of preview box Y shift title + + createPreviewMapButton(); + + // create Restore button + buttonObj = new createjs.Container(); + + //child id = 0 + var border = new createjs.Shape(); + border.graphics.beginFill(BUTTON_BORDER0_COLOR) + .drawRoundRect(-BUTTON_BORDER_SIZE/2, -BUTTON_BORDER_SIZE/2, buttonSizeX+BUTTON_BORDER_SIZE, MSG_AREA_Y+BUTTON_BORDER_SIZE ,BUTTON_ROUND_RADIUD).endFill(); + + //child id = 1 + var button = new createjs.Shape(); + button.graphics.beginFill(BUTTON_COLOR).drawRoundRect(0, 0, buttonSizeX, MSG_AREA_Y,BUTTON_ROUND_RADIUD).endFill(); + + //child id = 2 + buttonTextObj.textAlign = "center"; + buttonTextObj.x = buttonSizeX/2|0; + buttonTextObj.y = (MSG_AREA_Y - msgTextObj.getBounds().height)/2|0; + + buttonObj.addChild(border, button, buttonTextObj); + buttonObj.x = (menuX - buttonSizeX)/2|0; + buttonObj.y = fileInfoBoxYOffset + fileInfoBoxSizeY + MSG_GAP_Y|0; + + buttonObj.on('click', buttonClick); + buttonObj.on('mouseover', buttonMouseOver); + buttonObj.on('mouseout', buttonMouseOut); + buttonObj.alpha = 0; + dialogStage.addChild(buttonObj); + } + + function createPreviewMapButton() + { + var nextMapX = nextMapBitmap.getBounds().width*_scale, + nextMapY = nextMapBitmap.getBounds().height*_scale*2/3; + var borderNext = new createjs.Shape(); + var borderPrev = new createjs.Shape(); + + nextMapButtonObj = new createjs.Container(); + prevMapButtonObj = new createjs.Container(); + + //Preview Map Next button + borderNext.graphics.beginFill(PREVIEWMAP_BUTTON_COLOR) + .drawRoundRect(-PREVIEWMAP_BUTTON_BORDER_SIZE/2|0, -PREVIEWMAP_BUTTON_BORDER_SIZE/2|0, nextMapX+PREVIEWMAP_BUTTON_BORDER_SIZE, nextMapY+PREVIEWMAP_BUTTON_BORDER_SIZE, BUTTON_ROUND_RADIUD).endFill(); + borderNext.alpha =PREVIEWMAP_BUTTON_GRAY_ALPHA; + nextMapBitmap.setTransform(0, 0, _scale, _scale*2/3); + nextMapButtonObj.x = TITLE_TEXT_SIZE + (previewMapGapX + previewMapX)*previewMapPerLine - nextMapX-PREVIEWMAP_BUTTON_BORDER_SIZE/2; + nextMapButtonObj.y = previewMapYOffset + previewMapY + previewMapGapX/2; + nextMapButtonObj.myId = 1; + nextMapButtonObj.addChild(borderNext, nextMapBitmap); + nextMapButtonObj.on('click', PreviewButtonClick); + nextMapButtonObj.on('mouseover', PreviewButtonMouseOver); + nextMapButtonObj.on('mouseout', PreviewButtonMouseOut); + nextMapButtonObj.alpha = 0; + dialogStage.addChild(nextMapButtonObj); + + //Preview Map Prev Button + borderPrev.graphics.beginFill(PREVIEWMAP_BUTTON_COLOR) + .drawRoundRect(-PREVIEWMAP_BUTTON_BORDER_SIZE/2|0, -PREVIEWMAP_BUTTON_BORDER_SIZE/2|0, nextMapX+PREVIEWMAP_BUTTON_BORDER_SIZE, nextMapY+PREVIEWMAP_BUTTON_BORDER_SIZE, BUTTON_ROUND_RADIUD).endFill(); + borderPrev.alpha =PREVIEWMAP_BUTTON_GRAY_ALPHA; + prevMapBitmap.setTransform(0, 0, _scale, _scale*2/3); + prevMapButtonObj.x = TITLE_TEXT_SIZE + previewMapGapX + PREVIEWMAP_BUTTON_BORDER_SIZE/2; + prevMapButtonObj.y = previewMapYOffset + previewMapY + previewMapGapX/2; + prevMapButtonObj.myId = 2; + prevMapButtonObj.addChild(borderPrev, prevMapBitmap); + prevMapButtonObj.on('click', PreviewButtonClick); + prevMapButtonObj.on('mouseover', PreviewButtonMouseOver); + prevMapButtonObj.on('mouseout', PreviewButtonMouseOut); + prevMapButtonObj.alpha = 0; + dialogStage.addChild(prevMapButtonObj); + + function PreviewButtonClick() + { + var myId = this.myId; + var totalLevels = tmpCustomLevels.length; + switch(myId) { + case 1: //next page + previewMapFirst += previewMapPerLine; + previewMapLast += previewMapPerLine; + if (previewMapLast >= totalLevels) previewMapLast = totalLevels-1; + break; + case 2: //previous page + previewMapFirst -= previewMapPerLine; + previewMapLast = previewMapFirst + previewMapPerLine-1; + if (previewMapLast >= totalLevels) previewMapLast = totalLevels-1; + break; + default: + error("Error: Design Error myId = " + myId ); + return; + } + removePreviewMap(); + displayPreviewMap(myId); + } + + function PreviewButtonMouseOver() + { + var backgroundObj = this.getChildAt(0); + + backgroundObj.alpha = 1; + dialogStage.cursor = 'pointer'; + dialogStage.update(); + } + + function PreviewButtonMouseOut() + { + var backgroundObj = this.getChildAt(0); + + backgroundObj.alpha = PREVIEWMAP_BUTTON_GRAY_ALPHA; + dialogStage.cursor = 'default'; + dialogStage.update(); + } + + } + + function removePreviewObj() + { + if (wrongMsgEnable) { + dialogStage.removeChild(wrongText1Obj, wrongText2Obj); + //dialogStage.removeChild(wrongText2Obj); + wrongMsgEnable = 0; + } + removePreviewMap(); + disableMapButton(); + dialogStage.removeChild(totalLevelTextObj); + dialogStage.update(); + } + + function readRestoreFile(e) + { + var file; + var readFiler; + var fileSize, totalLevels, fileChecksum; + + removePreviewObj(); + buttonObj.alpha = 0; //disable restore button (hide) + + if (e.target.files.length <= 0) { + displayFileName(""); + return; + } + file = e.target.files[0]; + displayFileName(file.name); + + // (1) First only read header for check file format + readHeaderInfo(); + + //reference: https://developer.mozilla.org/en-US/docs/Web/API/FileReader + function readHeaderInfo() + { + var readHeader = new FileReader(); + var blob = file.slice(0, 150); //read 0 - 150 bytes + + readHeader.onloadend = function(e) { + if (e.target.readyState == FileReader.DONE) { + checkHeaderInfo(e.target.result); + } + }; + + readHeader.readAsText(blob); + } + + function checkHeaderInfo(headerInfo) + { + //LODE RUNNER WEB GAME.Sun May 16 2021 11:12:20 GMT+0800 (台北標準時間).SISS.MTAyMDAwNDA5YTg1NExS + var maxFileSize = 150 + NO_OF_TILES_X * NO_OF_TILES_Y * 256; + var newLinePos = headerInfo.indexOf('\n'); + if(newLinePos < 0) { + displayAlertMsg("wrong header"); + return; + } + + var lastDotPos = headerInfo.substr(0, newLinePos).lastIndexOf('.'); + if (lastDotPos < 0 || newLinePos - lastDotPos != 21) { + displayAlertMsg("wrong fileInfo"); + return; + } else { + try { + var fileInfo = atob(headerInfo.substr(lastDotPos+1, newLinePos-lastDotPos-1)); + } catch(e) { + // wrong base64 + displayAlertMsg("wrong base64"); + return; + } + var verInfo = fileInfo.charAt(0); + if ( verInfo != '1' && verInfo != '2') { + displayAlertMsg("wrong fileInfo.version (" + verInfo + ")"); + return; + } + if (headerInfo.indexOf(LRWG_FILE_START_INFO) != 0) { + displayAlertMsg("wrong startInfo"); + return; + } + totalLevels = parseInt(fileInfo.substr(1,2), 16); + fileSize = parseInt(fileInfo.substr(3,6), 16); + fileChecksum = parseInt(fileInfo.substr(9,4), 16); + if (totalLevels > MAX_EDIT_LEVEL || totalLevels <= 0) { + displayAlertMsg("wrong edit levels (" + totalLevels + ")"); + return; + } + if (file.size > maxFileSize) { + displayAlertMsg("Error: file size too large (" + file.size + ")"); + return; + } + if (fileSize != file.size) { + displayAlertMsg("wrong file size"); + return; + } + + // (2) Read all file + readAllFile = new FileReader(); + + readAllFile.onloadend = function(e) { + if (e.target.readyState == FileReader.DONE) { + readCustomLevels(e.target.result, verInfo); + } + } + readAllFile.readAsText(file); + } + } + + function readCustomLevels(fileData, verInfo) + { + var firstNewLinePos = fileData.indexOf('\n'); + var dataChecksum = 0; + var levelData = fileData.substr(firstNewLinePos); //skip header + + for(var i = 0; i < levelData.length; i++) { + dataChecksum += levelData.charCodeAt(i); + } + if (fileChecksum != (dataChecksum & 0xFFFF)) { + displayAlertMsg("wrong file checksum"); + return; + } + + tmpCustomLevels = []; + + if (verInfo == '1') { + // v1: level format + // ------------------------------- + // level map format: + // + // +----- startLevelPos + // V + // \n tile * NO_OF_TILES_X + // \n tile * NO_OF_TILES_X + // .... + // \n tile * NO_OF_TILES_X + // \n checksum + // ------------------------------ + var oneLevelSize = (NO_OF_TILES_X+1) * NO_OF_TILES_Y+2; // +2: checksum + '\n' + + if(levelData.length != totalLevels * oneLevelSize) { + displayAlertMsg("wrong levels data size (1)"); + return; + } + + for(var curLevel = 0; curLevel < totalLevels; curLevel++) { + var startLevelPos = curLevel * oneLevelSize + 1; // +1: skip first '\n' + var curLevelMap = ''; + for(var tileY = 0; tileY < NO_OF_TILES_Y; tileY++) { + // (startLevelPos+1), +1 : skip \n + curLevelMap += levelData.substr(startLevelPos+tileY*(NO_OF_TILES_X+1), NO_OF_TILES_X); + } + var checksum = levelData.charAt(startLevelPos+oneLevelSize-2); + var levelChecksum = getShareChecksum(curLevelMap); + if (checksum != levelChecksum) { + displayAlertMsg("wrong level checksum (" + (curLevel+1) + ")"); + return; + } + tmpCustomLevels.push(curLevelMap); + } + } else { + // v2: level format + // ----------------------------- + // \n zipLevelData + // \n zipLevelData + // ....... + // \n zipLevelData + // ----------------------------- + var startLevelPos = 1; // begin from pos = 1 + var nextNewLinePos; + var zipLevelData, curLevelMap; + for(var curLevel = 0; curLevel < totalLevels; curLevel++) { + nextNewLinePos = levelData.indexOf('\n', startLevelPos); + if (nextNewLinePos < 0) { + displayAlertMsg("wrong level data @" + (curLevel+1)); + return; + } + curLevelMap = unzipLevelMap(levelData.substr(startLevelPos, nextNewLinePos-startLevelPos)); + if (!curLevelMap) { + displayAlertMsg("wrong ziplevel data @" + (curLevel+1)); + return; + } + tmpCustomLevels.push(curLevelMap); + startLevelPos = nextNewLinePos+1; + } + if (levelData.length != startLevelPos) { + displayAlertMsg("wrong levels data size (2)"); + return; + } + } + + // SUCCESS + displayFileInfo(); + buttonObj.alpha = 1; //enable restore button + } + + function displayAlertMsg(errMsg) + { + debug(errMsg); + wrongMsgEnable = 1; + dialogStage.addChild(wrongText1Obj, wrongText2Obj); + dialogStage.update(); + } + } + + function displayFileName(fileName) + { + if (fileNameObj) { + dialogStage.removeChild(fileNameObj); + fileNameObj = null; + } + if (fileName.length <= 0) { + dialogStage.update(); + return; + } + + if (stringDisplayWidth(fileName) > MAX_DISPLAY_FILENAME_LEN) { + fileName = cutFileName(fileName); + } + fileNameObj = new createjs.Text(fileName, "bold " + MSG_TEXT_SIZE + "px Helvetica", MSG_TEXT_COLOR); + + if(fileNameObj.getBounds().width > maxFileNameSize) { + fileNameObj = new createjs.Text(fileName, "bold " + MSG_TEXT_SIZE + "px Courier New", MSG_TEXT_COLOR); + } + + fileNameObj.x = fileNameX; + fileNameObj.y = fileNameY; + dialogStage.addChild(fileNameObj); + dialogStage.update(); + + function stringDisplayWidth(str) + { + // 0x00-0xFF assume 1 char others assume 2 chars + //reference: https://blog.typeart.cc/JavaScript計算含中英文字的字串長度/ + return str.replace(/[^\x00-\xff]/g,"xx").length; + } + + function cutFileName(str) + { + var oneSideSize = (MAX_DISPLAY_FILENAME_LEN - 3)/2|0; + var leftName = '', rightName = ''; + + for(var i = 0, width=0; width < oneSideSize; i++, width++) { + var charCode = str.charCodeAt(i); + if (charCode <= 0xFF) { + leftName += str.charAt(i); + } else { + if(++width < oneSideSize) { + leftName += str.charAt(i); + } else { + break; + } + } + } + + for(var i = str.length-1, width=0; width < oneSideSize; i--, width++) { + var charCode = str.charCodeAt(i); + if (charCode <= 0xFF) { + rightName = str.charAt(i) + rightName; + } else { + if(++width < oneSideSize) { + rightName = str.charAt(i) + rightName; + } else { + break; + } + } + } + return leftName + "...." + rightName; + } + } + + function addOpenButton(xOffset, yOffset) + { + openFolderButtonObj = new createjs.Container(); + border = new createjs.Shape(); + button = new createjs.Shape(); + + //id = 0 + border.graphics.beginFill(OPENBUTTON_BACKGROUND_COLOR) + .drawRoundRect(-OPENBUTTON_BORDER_SIZE, -OPENBUTTON_BORDER_SIZE, openFolderButtonX+OPENBUTTON_BORDER_SIZE*2, openFolderButtonY+OPENBUTTON_BORDER_SIZE*2, OPENBUTTON_ROUND_RADIUD).endFill(); + //id = 1 + button.graphics.beginFill(OPENBUTTON_COLOR) + .drawRect(-OPENBUTTON_BORDER_SIZE/3, -OPENBUTTON_BORDER_SIZE/3, openFolderButtonX+OPENBUTTON_BORDER_SIZE*2/3, openFolderButtonY+OPENBUTTON_BORDER_SIZE*2/3).endFill(); + button.alpha = 0.9; + + //id = 2 + openFolderBitmap.setTransform(0, 0, _scale*openFolderButtonScale, _scale*openFolderButtonScale); + + openFolderButtonObj.x = xOffset; + openFolderButtonObj.y = yOffset; + openFolderButtonObj.addChild(border, button, openFolderBitmap); + + openFolderButtonObj.on('click', openButtonClick); + openFolderButtonObj.on('mouseover', openButtonMouseOver); + openFolderButtonObj.on('mouseout', openButtonMouseOut); + dialogStage.addChild(openFolderButtonObj); + + function openButtonClick() + { + inputFileElement.click(); + openButtonMouseOut(); + } + + function openButtonMouseOver() + { + var border = openFolderButtonObj.getChildAt(0); + + border.graphics.clear(); + border.graphics.beginFill(OPENBUTTON_BORDER_COLOR) + .drawRoundRect(-OPENBUTTON_BORDER_SIZE, -OPENBUTTON_BORDER_SIZE, + openFolderButtonX+OPENBUTTON_BORDER_SIZE*2, openFolderButtonY+OPENBUTTON_BORDER_SIZE*2, OPENBUTTON_ROUND_RADIUD).endFill(); + + dialogStage.cursor = 'pointer'; + dialogStage.update(); + } + + function openButtonMouseOut() + { + var border = openFolderButtonObj.getChildAt(0); + + border.graphics.clear(); + border.graphics.beginFill(OPENBUTTON_BACKGROUND_COLOR) + .drawRoundRect(-OPENBUTTON_BORDER_SIZE, -OPENBUTTON_BORDER_SIZE, + openFolderButtonX+OPENBUTTON_BORDER_SIZE*2, openFolderButtonY+OPENBUTTON_BORDER_SIZE*2, OPENBUTTON_ROUND_RADIUD).endFill(); + + dialogStage.cursor = 'default'; + dialogStage.update(); + } + + } + + function removePreviewMap() + { + for(var i=0 ; i < levelMapObjs.length; i++) { + dialogStage.removeChild(levelMapObjs[i]); + } + + for(var i=0 ; i < levelMapNoObj.length; i++) { + dialogStage.removeChild(levelMapNoObj[i]); + } + } + + function disableMapButton() + { + prevMapButtonObj.alpha = 0; //disable next map button (hide) + nextMapButtonObj.alpha = 0; //disable prev map button (hide) + } + + function displayPreviewMap(myId=0) + { + var borderSize = _scale < 1?1:_scale; + var scaleX = scaleY = PREVIEW_MAP_SCALE; + + levelMapObjs = []; + levelMapNoObj = []; + for(var level=previewMapFirst, i=0; level <= previewMapLast; level++, i++) { + var levelMap = new createjs.Container(); + var border = new createjs.Shape(); + var background = new createjs.Shape(); + var mapOffsetX = TITLE_TEXT_SIZE + previewMapGapX + (previewMapX+previewMapGapX) * i; + + levelMapNoObj[i] = new createjs.Text(("00"+(level+1)).slice(-3), "bold " + MAPNO_TEXT_SIZE + "px Helvetica", "black"); + levelMapNoObj[i].x = mapOffsetX; + levelMapNoObj[i].y = previewNoYOffset; + levelMapNoObj[i].alpha = 0.6; + dialogStage.addChild(levelMapNoObj[i]); + + border.graphics.beginFill("white") + .drawRoundRect(-borderSize, -borderSize, previewMapX+2*borderSize, previewMapY+2*borderSize, borderSize); + background.graphics.beginFill("black").drawRect(0, 0, previewMapX, previewMapY); + + levelMap.addChild(border, background, level2Bitmap(tmpCustomLevels[level], scaleX, scaleY)); + levelMapObjs[i] = levelMap; + levelMapObjs[i].x = mapOffsetX; + levelMapObjs[i].y = previewMapYOffset|0; + dialogStage.addChild(levelMapObjs[i]); + } + + if (previewMapFirst <= 0) { + prevMapButtonObj.alpha = 0; //disable + } else prevMapButtonObj.alpha = 1; //enable + + if (previewMapLast+1 >= tmpCustomLevels.length) { + nextMapButtonObj.alpha = 0; //disable + } else nextMapButtonObj.alpha = 1; //enable + dialogStage.update(); + } + + function displayFileInfo() + { + var totalLevels = tmpCustomLevels.length; + var levelText = totalLevels > 1? " Levels": " Level"; + + previewMapFirst = 0; + previewMapLast = previewMapPerLine-1; + if(previewMapLast+1 >= totalLevels) previewMapLast = totalLevels-1; + displayPreviewMap(); + + totalLevelTextObj = new createjs.Text("Total: " + tmpCustomLevels.length + levelText, "bold " + TOTALLEVEL_TEXT_SIZE + "px Helvetica", "black"); + totalLevelTextObj.x = (menuX-totalLevelTextObj.getBounds().width)/2|0; + totalLevelTextObj.y = previewMapYOffset + previewMapY + previewMapGapX*2; + totalLevelTextObj.alpha = 0.6; + dialogStage.addChild(totalLevelTextObj); + + dialogStage.update(); + } + + function level2Bitmap(levelMap, scaleX, scaleY) + { + var container = new createjs.Container(); + var guardCount = 0, runner = 0; + var bitmap; + + //-------------------------------------------- + // Parser map from right-bottom to left-top + // for drop guards if too manys + //-------------------------------------------- + var index = NO_OF_TILES_Y * NO_OF_TILES_X - 1; + for(var y = NO_OF_TILES_Y-1; y >= 0; y--) { + for(var x = NO_OF_TILES_X-1; x >= 0; x--) { + var id = levelMap.charAt(index--); + + var curTile; + switch(id) { + default: + case ' ': //empty + continue; + case '#': //Normal Brick + curTile = getThemeBitmap("brick"); + break; + case '@': //Solid Brick + curTile = getThemeBitmap("solid"); + break; + case 'H': //Ladder + curTile = getThemeBitmap("ladder"); + break; + case '-': //Line of rope + curTile = getThemeBitmap("rope"); + break; + case 'X': //False brick + curTile = getThemeBitmap("brick"); + break; + case 'S': //Ladder appears at end of level + continue; + case '$': //Gold chest + curTile = getThemeBitmap("gold"); + break; + case '0': //Guard + if(++guardCount > MAX_NEW_GUARD) { + continue; //too many guard , set this tile as empty + } + curTile = new createjs.Sprite(guardData, "runLeft"); + curTile.stop(); + break; + case '&': //Player + if(++runner > 1) { + continue; //too many runner, set this tile as empty + } + curTile = new createjs.Sprite(runnerData, "runRight"); + curTile.stop(); + break; + } + curTile.setTransform(x*BASE_TILE_X*scaleX, y*BASE_TILE_Y*scaleX, scaleX, scaleY); + container.addChild(curTile); + } + } + container.cache(0, 0, previewMapX, previewMapY); + bitmap = new createjs.Bitmap( container.cacheCanvas); // change "cont.getCacheDataURL()" to "cont.cacheCanvas" + container.removeAllChildren(); + return bitmap; + } + + function restoreConfirm(rc) + { + var tmpTestInfo = {level: 1}; // "level:1" for data safe + var oldEditLevels = editLevels; // 1 - + + if(rc) { //yes restore custom levels + + //------------------------------------------------------------------------------------ + //(1) clear edit levels + getTestLevel(tmpTestInfo); + clearEditLevelInfo(); //clear edit level info + clearStorage(STORAGE_USER_SCORE_INFO); //clear user score info + clearTestLevel(); //clear test level + initEditLevelInfo(); + + //(2) add levelmap to storage + editLevelData = []; + for(var i=0; i < tmpCustomLevels.length; i++) { + setEditLevel(++editLevels, tmpCustomLevels[i]); //editLevels: 1 - MAX_EDIT_LEVEL + } + + //(3) save edit info + setEditLevelInfo(); + if(tmpTestInfo.level > oldEditLevels && editLevels < MAX_EDIT_LEVEL && !levelMapIsEmpty(tmpTestInfo.levelMap)) { + //still have space for save new editing level + tmpTestInfo.level = editLevels+1; + setTestLevel(tmpTestInfo); + } + //----------------------------------------------------------------------------------- + + restoreKeyState(); + curLevel = 1; + setModernInfo(); // set current level = 1 for play mode + if(playMode == PLAY_EDIT) { + editEdit(-1, _callBackFun); + } else { + editPlay(-1, _callBackFun); + } + } else { + restoreKeyState(); + if(_callBackFun) _callBackFun(); + + } + } + + //restore button click + function buttonClick() + { + removeCanvas(); + removeCoverBackground(); + yesNoDialog(["Warning", "Overwrite existing custom levels ?"], yesBitmap, noBitmap, mainStage, tileScale, restoreConfirm); + } + + //restore button mouse over + function buttonMouseOver() + { + var border = buttonObj.getChildAt(0); + + border.graphics.clear(); + border.graphics.beginFill(BUTTON_BORDER1_COLOR) + .drawRoundRect(-BUTTON_BORDER_SIZE, -BUTTON_BORDER_SIZE, buttonSizeX+BUTTON_BORDER_SIZE*2, MSG_AREA_Y+BUTTON_BORDER_SIZE*2,BUTTON_ROUND_RADIUD).endFill(); + + buttonTextObj.shadow = new createjs.Shadow(TITLE_TEXT_SHADOW_COLOR, 2, 2, 10 ); + dialogStage.cursor = 'pointer'; + dialogStage.update(); + } + + //restore button mouse out + function buttonMouseOut() + { + var border = buttonObj.getChildAt(0); + + border.graphics.clear(); + border.graphics.beginFill(BUTTON_BORDER0_COLOR) + .drawRoundRect(-BUTTON_BORDER_SIZE/2, -BUTTON_BORDER_SIZE/2, buttonSizeX+BUTTON_BORDER_SIZE, MSG_AREA_Y+BUTTON_BORDER_SIZE, BUTTON_ROUND_RADIUD).endFill(); + + buttonTextObj.shadow = null; + dialogStage.cursor = 'default'; + dialogStage.update(); + } + + function removeCanvas() + { + dialogStage.removeAllChildren(); + dialogStage.enableMouseOver(0); + createjs.Ticker.removeEventListener(dialogStage); + document.body.removeChild(restoreCanvas); + } + + function removeCoverBackground() + { + mainStage.removeChild(coverBackgroundObj); + } + + function closeDialog() + { + restoreKeyState(); + removeCanvas(); + removeCoverBackground(); + if(_callBackFun) _callBackFun(); + } + + function handleEscKeyDown(event) + { + if(!event){ event = window.event; } //cross browser issues exist + + if(event.shiftKey || event.ctrlKey) return false; + + switch(event.keyCode) { + case KEYCODE_ESC: + closeDialog(); + break; + default: + //debug("keycode = " + code); + break; + } + return false; + } +} diff --git a/lodeRunner.misc.js b/lodeRunner.misc.js index f86b2da..680b4d1 100644 --- a/lodeRunner.misc.js +++ b/lodeRunner.misc.js @@ -14,9 +14,9 @@ function assert(expression, msg) console.assert(expression, msg); } -function error(funName, string) +function error(string) { - console.log("Error In " + funName + "( ): " + string); + console.log("Error In " + error.caller.name + "( ): " + string); } function getScreenSize() @@ -49,8 +49,11 @@ function getScreenSize() //=============================== function showTipsMsg(_tipsTxt, _stage, _scale, _tipsTxt1) { - var TEXT_SIZE = 72* _scale; - var TEXT_COLOR = "#ff2020"; + var TEXT_SIZE = 72 * _scale; + var TEXT1_SIZE = 66 * _scale; + var TEXT_COLOR = "#FF2020"; + var TEXT1_COLOR = "#FF2020"; + var tipsText = new createjs.Text(_tipsTxt, "bold " + TEXT_SIZE + "px Helvetica", TEXT_COLOR); var screenX1 = _stage.canvas.width; var screenY1 = _stage.canvas.height; @@ -67,7 +70,7 @@ function showTipsMsg(_tipsTxt, _stage, _scale, _tipsTxt1) if(_tipsTxt1 != null) { // two tips - var tipsText1 = new createjs.Text(_tipsTxt1, "bold " + TEXT_SIZE + "px Helvetica", TEXT_COLOR); + var tipsText1 = new createjs.Text(_tipsTxt1, "bold " + TEXT1_SIZE + "px Helvetica", TEXT1_COLOR); tipsText1.x = (screenX1) / 2 | 0; tipsText1.y = screenY1/2 | 0; @@ -172,14 +175,30 @@ function phpTimeZone() //======================================== // get local time (YYYY-MM-DD HH:MM:SS) //======================================== -function getLocalTime() +function getLocalTime(d) { - var d = new Date(); + if (d == null) d = new Date(); return (("000"+d.getFullYear()).slice(-4)+ "-" +("0"+(d.getMonth() + 1)).slice(-2)+ "-" +("0"+d.getDate()).slice(-2)+ " " + ("0"+d.getHours()).slice(-2) + ":" + ("0"+d.getMinutes()).slice(-2) + ":" + ("0"+d.getSeconds()).slice(-2)); } +// ====================================================================== +// return format : "2021-06-08 12:09:08 GMT+0800 (Taipei Standard Time)" +// ====================================================================== +function getLocalTimeZone(d) +{ + if (d == null) d = new Date(); + + var dString = d.toString(); // format: "Tue Jun 08 2021 12:09:08 GMT+0800 (台北標準時間)" + var dGMTOffset = dString.indexOf('GMT'); + var dGMT = "" + + if(dGMTOffset >= 0) dGMT = ' ' + dString.substr(dGMTOffset); + + return getLocalTime(d) + dGMT; +} + //=============== // Random Object //=============== diff --git a/lodeRunner.preload.js b/lodeRunner.preload.js index ac98f50..7811ae7 100644 --- a/lodeRunner.preload.js +++ b/lodeRunner.preload.js @@ -21,6 +21,7 @@ var helpObj, helpBitmap, editHelpBitmap; var mainMenuIconBitmap, mainMenuIconObj; var selectIconBitmap, selectIconObj; var demoIconBitmap, demoIconObj; +var pasteIconBitmap, pasteIconObj; // paste icon var soundOnIconBitmap, soundOffIconBitmap, soundIconObj; var helpIconBitmap, helpIconObj; var infoObj, infoIconBitmap, infoIconObj; @@ -28,6 +29,8 @@ var repeatActionOnIconBitmap, repeatActionOffIconBitmap, repeatActionIconObj; var apple2IconBitmap, C64IconBitmap, themeIconObj, themeColorObj; var checkBitmap; var returnBitmap, select1Bitmap, nextBitmap; +var openFolderBitmap; // for restore custom levels +var nextMapBitmap, prevMapBitmap; // for restore custom levels var yesBitmap, noBitmap; var coverPageLoad; var noCache = "?" + VERSION+ ".1051230"; @@ -199,11 +202,17 @@ function preloadResource() { src: "image/return.png"+noCache, id: "return" }, { src: "image/select1.png"+noCache, id: "select1" }, { src: "image/next.png"+noCache, id: "next" }, + + { src: "image/openFolder.png"+noCache, id: "openFolder" }, //05/17/2021 + { src: "image/nextMap.png"+noCache, id: "nextMap" }, //05/21/2021 + { src: "image/prevMap.png"+noCache, id: "prevMap" }, //05/21/2021 { src: "image/yes.png"+noCache, id: "yes" }, { src: "image/no.png"+noCache, id: "no" }, { src: "image/demo.png"+noCache,id: "demo" }, + + { src: "image/paste.png"+noCache,id: "paste" }, //04/23/2021 { src: "image/soundOn.png"+noCache, id: "soundOn" }, { src: "image/soundOff.png"+noCache, id: "soundOff" }, @@ -287,7 +296,7 @@ function preloadResource() //Set percentTxt position percentTxt.x = (canvas.width - percentTxt.getBounds().width) / 2 | 0; - percentTxt.y = (BASE_SCREEN_Y - COVER_PROGRESS_UNDER_Y) * tileScale; + percentTxt.y = (BASE_SCREEN_Y - COVER_PROGRESS_UNDER_Y) * tileScale + height/12; // move percent number Lower mainStage.addChild(runnerSprite, progress, progressBorder, percentTxt); @@ -383,6 +392,9 @@ function preloadResource() demoIconBitmap = new createjs.Bitmap(preload.getResult("demo")); //04/18/2015 demoIconObj = new demoIconClass(screenX1, screenY1, tileScale, demoIconBitmap); + + pasteIconBitmap = new createjs.Bitmap(preload.getResult("paste")); //04/23/2021 + pasteIconObj = new pasteIconClass(screenX1, screenY1, tileScale, pasteIconBitmap); soundOnIconBitmap = new createjs.Bitmap(preload.getResult("soundOn")); //04/18/2015 soundOffIconBitmap = new createjs.Bitmap(preload.getResult("soundOff")); @@ -410,6 +422,10 @@ function preloadResource() select1Bitmap = new createjs.Bitmap(preload.getResult("select1")); nextBitmap = new createjs.Bitmap(preload.getResult("next")); + openFolderBitmap = new createjs.Bitmap(preload.getResult("openFolder")); // for restore custom levels + nextMapBitmap = new createjs.Bitmap(preload.getResult("nextMap")); // for restore custom levels + prevMapBitmap = new createjs.Bitmap(preload.getResult("prevMap")); + yesBitmap = new createjs.Bitmap(preload.getResult("yes")); noBitmap = new createjs.Bitmap(preload.getResult("no")); diff --git a/lodeRunner.runner.js b/lodeRunner.runner.js index 4ff290d..dd0ea8a 100644 --- a/lodeRunner.runner.js +++ b/lodeRunner.runner.js @@ -686,7 +686,7 @@ function removeFillHoleObj(spriteObj) return; } } - error(arguments.callee.name, "design error !"); + error("design error !"); } //======================================= diff --git a/lodeRunner.share.js b/lodeRunner.share.js new file mode 100644 index 0000000..503a50e --- /dev/null +++ b/lodeRunner.share.js @@ -0,0 +1,340 @@ +//================================================================================= +// For share custom level to other players +// -------------------------------------------------------------------------------- +// Version 1 url parameter format: +// +// ?shareLevel=[LEVEL DATA].[s]([v][m][t][r][creator][pading]) +// +// [LEVEL DATA] : ZIP level data +// [s]: checksum ([v]..[pading]) +// [v]: version = 1 [0] +// [m]: share mode (1:Fackbook, 2:twitter, 3:weibo, 4:link) [1] +// [t]: theme mode (1:apple2, 2:c64) [2] +// [r]: reserved 3 bytes [3..5] +// [creator]: creator name [6-] +// [pading]: pading "@" let length([v]+[m]+[t]+[creator]+[pading]) multiplier of 3 +//================================================================================= + +var SHARE_DATA_VERSION = 1; +var shareLevelData = [""]; +var shareInfo = null; // { ver:1, mode:1-4 [m], theme:1-2 [t], creator: } +var shareFirstTime = 1; + +function getQueryStringParameterValue(queryString, parameterName) +{ + var ParameterValue = ""; + var valueLength = 0 + queryString = decodeURIComponent(queryString); //url decode + var beginIdxOfParameter = queryString.indexOf(parameterName + "="); + if (beginIdxOfParameter >= 0) { + var startIdx = beginIdxOfParameter + parameterName.length + 1; // skip "parameterName=" + for (var endIdx=startIdx; + queryString.charAt(endIdx) !='&' && endIdx < queryString.length; endIdx++) valueLength++; + ParameterValue = queryString.substr(startIdx, valueLength); + } + return ParameterValue; +} + +function getShareChecksum(strData) +{ + var checksum = 0; + for(var i = 0; i < strData.length; i++) { + checksum += strData.charCodeAt(i); + } + return value2OutValue((checksum & 0x1F)+1); //1..32 +} + +function getShareVerInfo(encUrlVerData) +{ + var verInfo = null; + var infoChecksum = encUrlVerData.charAt(0); + var urlVerData = atob(encUrlVerData.substr(1,)); //base64 decode + var strChecksum = getShareChecksum(urlVerData); + var version = urlVerData.charAt(0); + + if(infoChecksum == strChecksum && (urlVerData.length % 3) == 0 && urlVerData.length >= 9 && version == '1') { + var shareMode = urlVerData.charCodeAt(1)-'0'.charCodeAt(0); //[m] + var theme = urlVerData.charCodeAt(2)-'0'.charCodeAt(0); //[t] + var creator = urlVerData.substr(6,); //[creator] + var creatorLength = creator.length; + + // remove last '@' chars + while(creatorLength > 0) { + if (creator.charAt(creatorLength-1) == '@') creatorLength--; + else break; + } + if (creator.length != creatorLength) + creator = creator.substr(0, creatorLength); + + if(shareMode > 0 && shareMode <= 4 && theme > 0 && theme <=2 && creatorLength > 0 && vaildPlayerName(creator)) { + verInfo = {}; + verInfo.ver = 1; + verInfo.mode = shareMode; + verInfo.theme = theme; + verInfo.creator = creator; + } + } + + if(!verInfo) error("wrong verInfo"); + + return verInfo; +} + +function getShareUrlInfo() +{ + if (!shareFirstTime) return; + shareFirstTime = 0; + + var shareUrlData = getQueryStringParameterValue(location.search, "shareLevel"); + + if (shareUrlData) { + var dotPos = shareUrlData.indexOf("."); + if (dotPos > 0) { + var unzipLevel = unzipLevelMap(shareUrlData.substr(0, dotPos)); + + if(unzipLevel && shareUrlData.length > dotPos) { + shareInfo = getShareVerInfo(shareUrlData.substr(dotPos+1,)); + } + + if (shareInfo) { + shareLevelData[0] = unzipLevel; + copyShareLevelForEditing(); + + playMode = PLAY_MODERN; + playData = PLAY_DATA_SHARE; + + ///////////////////////////////////////////////// + + sendShareLevel2Server(unzipLevel, shareInfo, null); //new or update share count + + ///////////////////////////////////////////////// + } + + // remove "share.html" + var removeString = "/share.html"; + var removeLength = removeString.length; + var newPathName = location.pathname; + if(newPathName.substr(-removeLength) == removeString) + newPathName = newPathName.substr(0, newPathName.length-removeLength+1); + //console.log(newPathName); + + // change URL to normal path (adds an entry to the browser's session history stack) + history.pushState({}, "Lode Runner Web Game", newPathName); + } + } +} + +function converLevel2ShareUrl(level, shareMode, creator) +{ + var theme =(curTheme == THEME_APPLE2)?1:2; + var padingTimes = (3-creator.length%3) % 3; + var zipLevel = zipLevelMap(level); + var verInfo = `1${shareMode}${theme} ${creator}${'@'.repeat(padingTimes)}`; + var checksum = getShareChecksum(verInfo); + + var leftSlash = location.pathname.lastIndexOf("/"); + var pathName = leftSlash < 0 ? "/": location.pathname.substr(0,leftSlash+1); + var host = location.host; + + //host = "loderunnerwebgame.com/game"; + + return location.protocol + "//" + host + pathName + "share.html?shareLevel=" + zipLevel + "." + checksum + encodeURIComponent(btoa(verInfo)); +} + +var zipIdToMap = [' ', '#', '@', 'H', '-', 'X', 'S', '$', '0', '&']; +var mapToZipId = {' ':0, '#':1, '@':2, 'H':3, '-': 4, 'X':5, 'S':6, '$':7, '0':8, '&':9 } + +// value range [1..52] ==> A..Z + a..z +// (01-26) ==> 'A'-'Z' (0x41 - 0x5A) +// (27-52) ==> 'a'-'z' (0x61 - 0x7A) +function value2OutValue(value) +{ + assert(value >= 1 && value <= 52 , "Error: value not in range 1..52"); + + if (value <= 26) { + var outValue = String.fromCharCode(value-1 + 'A'.charCodeAt(0)); //'A' ==> 1 , 'B' ==> 2 ... + } else { + var outValue = String.fromCharCode(value-27 + 'a'.charCodeAt(0)); //'a' ==> 27 , 'b' ==> 28 ... + } + return outValue; +} + +function appendZipTile(tileType, tileCount) +{ + var rcTile = ''; + + var outTile = String.fromCharCode(mapToZipId[tileType] + '0'.charCodeAt(0)); //0 ==> '0' , 1 ==> '1' + switch(tileCount) { + case 0: + error("design error, tileType = None"); + break; + case 1: + rcTile = outTile; + break + case 2: + rcTile = outTile + outTile; + break; + default: + outCount = value2OutValue(tileCount); + rcTile = outCount + outTile + break; + } + return rcTile; +} + +function zipLevelMap(level) +{ + var zipLevel = ""; + + assert(level.length == (NO_OF_TILES_X * NO_OF_TILES_Y), "Error: zipShareLevel tiles != totalTiles"); + + var lastTile = ''; // begin with a fake tile + var tileCount = 0; + var checksum = 0; // checksum + + for(var tileIdx = 0; tileIdx < level.length; tileIdx++) { + curTile = level.charAt(tileIdx); + checksum += mapToZipId[curTile]; //XOR + if (lastTile == curTile) { + tileCount += 1; + if (tileCount < 52) continue; // the maximun compress count = 52 (a-z + A-Z) + else curTile = ''; //fake tile + } + + if (tileCount > 0) zipLevel += appendZipTile(lastTile, tileCount); + + lastTile = curTile; + if (lastTile == '') tileCount = 0; // compress count > max compress count (append a fake tile) + else tileCount = 1; + } + + if (tileCount > 0) zipLevel += appendZipTile(lastTile, tileCount); + zipLevel += value2OutValue((checksum & 0x1F)+1); // checksum 1..32 + + return zipLevel; + +} + +function unzipLevelMap(zipLevel) +{ + var dupNum = 1; + var unzipLevel = ''; + var checksum = 0; + var caseType = 1; + + for (var curIdx = 0; curIdx < zipLevel.length && caseType > 0; curIdx++) { + curChar = zipLevel.charAt(curIdx); + switch (true) { + case curChar >= '0' && curChar <= '9': // tileType + caseType = 1; + tileIdx = curChar.charCodeAt(0) - '0'.charCodeAt(0); + unzipLevel += Array(dupNum+1).join(zipIdToMap[tileIdx]); // char * dupNum + checksum += (tileIdx * dupNum); + dupNum = 1 + break; + case curChar >= 'A' && curChar <= 'Z': // dup number 1-26 + if (dupNum > 1) { + caseType = -1; //error + error("Error: before A-Z still a dupNumber"); + } else { + caseType = 2; + dupNum = curChar.charCodeAt(0) - 'A'.charCodeAt(0) + 1; + } + break; + case curChar >= 'a' && curChar <= 'z': // dup number 27-52 + if (dupNum > 1) { + caseType = -1; //error + error("Error: before a-z still a dupNumber"); + } else { + caseType = 2; + dupNum = curChar.charCodeAt(0) - 'a'.charCodeAt(0) + 27; + } + break; + default: + caseType = -1; //error + error("Error: wrong tileType = '" + curChar + "'"); + break; + } + } + + if (caseType < 0) return ''; // Error + + checksum = (checksum & 0x1F) + 1; // 1..32 + + if(caseType != 2 || checksum != dupNum) { + error("Error: wrong checksum !"); + return ''; + } else if (unzipLevel.length != (NO_OF_TILES_X * NO_OF_TILES_Y)) { + error("Error: wrong tile size (" + unzipLevel.length + ") !"); + return ''; + } + + return unzipLevel; +} + +// ============================================================================================== +// Generate custom level backup data +// +// (1) HeaderInfo: "LODE RUNNER WEB GAME.2021-06-08 12:09:08 GMT+0800 (台北標準時間).player." +// (2) zipInfo: [v][l][s][c] +// +// [v]: version [0] ==> 1 or 2 +// [l]: levels [1:2] ==> [01-FF] +// [s]: size [3:8] ==> [000000-FFFFFF] +// [c]: checksum [9:12] ==> [0000-FFFF] +// [r]: reserved [13:14] ==> "LR" +// +// (3) level map data: +// ------------------------------ +// v1: +// +----- startLevelPos +// V +// \n tile * NO_OF_TILES_X +// \n tile * NO_OF_TILES_X +// .... +// \n tile * NO_OF_TILES_X +// \n checksum +// ------------------------------ +// v2: +// \n zipLevelData +// \n zipLevelData +// ....... +// \n zipLevelData +// \n +// ------------------------------ +// ============================================================================================== +function backupCustomLevelData(date) +{ + var curTimeInfo = getLocalTimeZone(date); + var headerInfo = LRWG_FILE_START_INFO + curTimeInfo + "." + playerName + "."; + var levelSize = ("0" + editLevels.toString(16)).slice(-2); + var fileSize = encodeURI(headerInfo).replace(/%../g,'.').length + 20; //20 : length of zipInfo + var zipLevelData = ""; + var zipChecksum = 0; + + /* version 1 + var verInfo = '1'; + for(var i = 0; i < editLevels; i++) { + var curLevel = editLevelData[i]; + for(var startPos = 0; startPos < curLevel.length; startPos += NO_OF_TILES_X) { + zipLevelData += '\n' + curLevel.substr(startPos, NO_OF_TILES_X); + } + zipLevelData += '\n' + getShareChecksum(curLevel); + } + */ + + var verInfo = '2'; + for(var i = 0; i < editLevels; i++) { + zipLevelData += '\n' + zipLevelMap(editLevelData[i]); // \nzipLevel \nzipLevel.... \nzipLevel + } + zipLevelData += '\n'; // \nzipLevel \nzipLevel.... \nzipLevel \n + + for(var i = 0; i < zipLevelData.length; i++) { + zipChecksum += zipLevelData.charCodeAt(i); + } + + zipChecksum = ("000" + (zipChecksum & 0xFFFF).toString(16)).slice(-4); + fileSize = ("00000" + (fileSize + zipLevelData.length).toString(16)).slice(-6); + var zipInfo = btoa(verInfo + levelSize + fileSize + zipChecksum + "LR"); + return headerInfo + zipInfo + zipLevelData; +} diff --git a/lodeRunner.storage.js b/lodeRunner.storage.js index d4a6420..4da3d65 100644 --- a/lodeRunner.storage.js +++ b/lodeRunner.storage.js @@ -63,7 +63,7 @@ function getClassicInfo() // END for support base64 encode //================================ } else { - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); playData = 1; } levelData = getPlayVerData(playData); @@ -104,7 +104,7 @@ function setClassicInfo(passed) if(playData >= 1 && playData <= maxPlayId) { setStorage(STORAGE_CLASSIC_INFO+playData, infoJSON); } else { - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); } } @@ -128,13 +128,13 @@ function getModernInfo() levelData = editLevelData; } else { //no any user created level ! playData = 1; - playData2MainMenuId(); + playData2GameVersionMenuId(); infoJSON = getStorage(STORAGE_MODERN_INFO+1); levelData = getPlayVerData(playData); } break; default: - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); playData = 1; break; } @@ -165,7 +165,7 @@ function setModernInfo() setStorage(STORAGE_USER_INFO, infoJSON); //user created break; default: - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); break; } } @@ -180,7 +180,7 @@ function clearModernInfo() clearStorage(STORAGE_USER_INFO); break; default: - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); break; } } @@ -199,7 +199,7 @@ function getModernScoreInfo() levelSize = MAX_EDIT_LEVEL; break; default: - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); break; } @@ -225,7 +225,7 @@ function setModernScoreInfo() setStorage(STORAGE_USER_SCORE_INFO, infoJSON); break; default: - error(arguments.callee.name, "design error, value =" + playData ); + error("design error, value =" + playData ); break; } } @@ -376,6 +376,17 @@ function getTestLevel(testInfo) } else { var init = testInfo.level<0?1:0; var infoObj = JSON.parse(infoJSON); + + //add statements for testInfo safer + if(!('level' in infoObj) || !('map' in infoObj) || !('pass' in infoObj) || + infoObj.level < 0 || infoObj.level > editLevels+1 || + infoObj.map.length != NO_OF_TILES_X * NO_OF_TILES_Y) + { + //wrong map data clear it + clearTestLevel(); + initNewLevelInfo(testInfo); + return; + } testInfo.level = infoObj.level; testInfo.levelMap = infoObj.map; @@ -389,18 +400,19 @@ function getTestLevel(testInfo) } //BEGIN for debug ==================== - var i = 0; - for(var y = 0; y < NO_OF_TILES_Y; y++) { - var string = "" - for(var x = 0; x < NO_OF_TILES_X; x++) { - string += testInfo.levelMap[i++]; - } - //debug('"' + string + '"'); - } + //var i = 0; + //for(var y = 0; y < NO_OF_TILES_Y; y++) { + // var string = "" + // for(var x = 0; x < NO_OF_TILES_X; x++) { + // string += testInfo.levelMap[i++]; + // } + // //debug('"' + string + '"'); + //} //END for debug ==================== - if(testInfo.level > editLevels) testInfo.modified = 1; - else { + if(testInfo.level > editLevels) { + testInfo.modified = 1; + } else { if(compareWithExist(editLevelData[infoObj.level-1], testInfo.levelMap) == 0) { if(init) { //same as exist level, new level @@ -411,7 +423,6 @@ function getTestLevel(testInfo) testInfo.modified = 1; } } - } }