-| (1) |
+1. |
 |
APPLE-II |
-| (2) |
+2. |
 |
Commodore 64 |
@@ -76,19 +76,22 @@ This program build with Javascript + [CreateJS](http://www.createjs.com).
-| (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;
}
}
-
}
}