dc96a5ac8d
(1) Modified for AI VERSION = 4
(1.1) When guard failing in hole, will drop gold before guard failing into hole totally
(1.2) Shake time extension when guard in hole.
NOW the runner can dig three hold for three guards and pass through them. (MUST quickly)
==> The behavior almost same as original lode runner (APPLE-II version).
Modified files: LodeRunner/lodeRunner.guard.js
(2) New Demo Data all ai_version = 4
modified files: lodeRunner.demoData1.js
(3) Slow down play speed:
//slow normal fast
change from speedMode = [15, 20, 25, 30, 35];
to speedMode = [14, 18, 23, 29, 35];
Modified files: lodeRunner.main.js
(4) Keep ai version don't overflow:
Modified files: lodeRunner.preload.js
960 lignes
28 KiB
JavaScript
960 lignes
28 KiB
JavaScript
//=============================================================================
|
|
// AI reference book: "玩 Lode Runner 學 C 語言"
|
|
// http://www.kingstone.com.tw/book/book_page.asp?kmcode=2014710650538
|
|
//=============================================================================
|
|
|
|
var movePolicy = [ [0, 0, 0, 0, 0, 0], //* move_map is used to find *//
|
|
[0, 1, 1, 0, 1, 1], //* wheather to move a enm *//
|
|
[1, 1, 1, 1, 1, 1], //* by indexing into it with *//
|
|
[1, 2, 1, 1, 2, 1], //* enm_byte + num_enm + *//
|
|
[1, 2, 2, 1, 2, 2], //* set_num to get a byte. *//
|
|
[2, 2, 2, 2, 2, 2], //* then that byte is checked*//
|
|
[2, 2, 3, 2, 2, 3], //* for !=0 and then decrmnt *//
|
|
[2, 3, 3, 2, 3, 3], //* for next test until = 0 *//
|
|
[3, 3, 3, 3, 3, 3],
|
|
[3, 3, 4, 3, 3, 4],
|
|
[3, 4, 4, 3, 4, 4],
|
|
[4, 4, 4, 4, 4, 4]
|
|
];
|
|
|
|
var moveOffset = 0;
|
|
var moveId = 0; //current guard id
|
|
|
|
var numOfMoveItems = movePolicy[0].length;
|
|
|
|
//********************************
|
|
//initial guard start move value
|
|
//********************************
|
|
function initGuardVariable()
|
|
{
|
|
moveOffset = moveId = 0;
|
|
}
|
|
|
|
function moveGuard()
|
|
{
|
|
var moves;
|
|
var curGuard;
|
|
var x, y, yOffset;
|
|
|
|
if(!guardCount) return; //no guard
|
|
|
|
if( ++moveOffset >= numOfMoveItems ) moveOffset = 0;
|
|
moves = movePolicy[guardCount][moveOffset]; // get next moves
|
|
|
|
while ( moves-- > 0) { // slows guard relative to runner
|
|
if(++moveId >= guardCount) moveId = 0;
|
|
curGuard = guard[moveId];
|
|
|
|
if(curGuard.action == ACT_IN_HOLE || curGuard.action == ACT_REBORN) {
|
|
continue;
|
|
}
|
|
|
|
guardMoveStep(moveId, bestMove(moveId));
|
|
}
|
|
}
|
|
|
|
function guardMoveStep( id, action)
|
|
{
|
|
var curGuard = guard[id];
|
|
var x = curGuard.pos.x;
|
|
var xOffset = curGuard.pos.xOffset;
|
|
var y = curGuard.pos.y;
|
|
var yOffset = curGuard.pos.yOffset;
|
|
|
|
var curToken, nextToken;
|
|
var centerX, centerY;
|
|
var curShape, newShape;
|
|
var stayCurrPos;
|
|
|
|
centerX = centerY = ACT_STOP;
|
|
curShape = newShape = curGuard.shape;
|
|
|
|
if(curGuard.action == ACT_CLIMB_OUT && action == ACT_STOP)
|
|
curGuard.action = ACT_STOP; //for level 16, 30, guard will stock in hole
|
|
|
|
switch(action) {
|
|
case ACT_UP:
|
|
case ACT_DOWN:
|
|
case ACT_FALL:
|
|
if ( action == ACT_UP ) {
|
|
stayCurrPos = ( y <= 0 ||
|
|
(nextToken = map[x][y-1].act) == BLOCK_T ||
|
|
nextToken == SOLID_T || nextToken == TRAP_T ||
|
|
nextToken == GUARD_T);
|
|
|
|
if( yOffset <= 0 && stayCurrPos)
|
|
action = ACT_STOP;
|
|
} else { //ACT_DOWN || ACT_FALL
|
|
stayCurrPos = ( y >= maxTileY ||
|
|
(nextToken = map[x][y+1].act) == BLOCK_T ||
|
|
nextToken == SOLID_T || nextToken == GUARD_T);
|
|
|
|
if( action == ACT_FALL && yOffset < 0 && map[x][y].base == BLOCK_T) {
|
|
action = ACT_IN_HOLE;
|
|
stayCurrPos = 1;
|
|
} else {
|
|
if ( yOffset >= 0 && stayCurrPos)
|
|
action = ACT_STOP;
|
|
}
|
|
}
|
|
|
|
if ( action != ACT_STOP ) {
|
|
if ( xOffset > 0)
|
|
centerX = ACT_LEFT;
|
|
else if ( xOffset < 0)
|
|
centerX = ACT_RIGHT;
|
|
}
|
|
break;
|
|
case ACT_LEFT:
|
|
case ACT_RIGHT:
|
|
if ( action == ACT_LEFT ) {
|
|
/* original source code from book
|
|
stayCurrPos = ( x <= 0 ||
|
|
(nextToken = map[x-1][y].act) == BLOCK_T ||
|
|
nextToken == SOLID_T || nextToken == TRAP_T ||
|
|
nextToken == GUARD_T);
|
|
*/
|
|
// change check TRAP_T from base,
|
|
// for support level 41==> runner in trap will cause guard move
|
|
stayCurrPos = ( x <= 0 ||
|
|
(nextToken = map[x-1][y].act) == BLOCK_T ||
|
|
nextToken == SOLID_T || nextToken == GUARD_T ||
|
|
map[x-1][y].base == TRAP_T);
|
|
|
|
if (xOffset <= 0 && stayCurrPos)
|
|
action = ACT_STOP;
|
|
} else { //ACT_RIGHT
|
|
/* original source code from book
|
|
stayCurrPos = ( x >= maxTileX ||
|
|
(nextToken = map[x+1][y].act) == BLOCK_T ||
|
|
nextToken == SOLID_T || nextToken == TRAP_T ||
|
|
nextToken == GUARD_T);
|
|
*/
|
|
// change check TRAP_T from base,
|
|
// for support level 41==> runner in trap will cause guard move
|
|
stayCurrPos = ( x >= maxTileX ||
|
|
(nextToken = map[x+1][y].act) == BLOCK_T ||
|
|
nextToken == SOLID_T || nextToken == GUARD_T ||
|
|
map[x+1][y].base == TRAP_T);
|
|
|
|
if (xOffset >= 0 && stayCurrPos)
|
|
action = ACT_STOP;
|
|
}
|
|
|
|
if ( action != ACT_STOP ) {
|
|
if ( yOffset > 0 )
|
|
centerY = ACT_UP;
|
|
else if ( yOffset < 0)
|
|
centerY = ACT_DOWN;
|
|
}
|
|
break;
|
|
}
|
|
|
|
curToken = map[x][y].base;
|
|
|
|
if ( action == ACT_UP ) {
|
|
yOffset -= yMove;
|
|
|
|
if(stayCurrPos && yOffset < 0) yOffset = 0; //stay on current position
|
|
else if(yOffset < -H2) { //move to y-1 position
|
|
if ( curToken == BLOCK_T || curToken == HLADR_T ) curToken = EMPTY_T; //in hole or hide laddr
|
|
map[x][y].act = curToken; //runner move to [x][y-1], so set [x][y].act to previous state
|
|
y--;
|
|
yOffset = tileH + yOffset;
|
|
if(map[x][y].act == RUNNER_T) setRunnerDead(); //collision
|
|
//map[x][y].act = GUARD_T;
|
|
}
|
|
|
|
if( yOffset <= 0 && yOffset > -yMove) {
|
|
dropGold(id); //decrease count
|
|
}
|
|
newShape = "runUpDn";
|
|
}
|
|
|
|
if ( centerY == ACT_UP ) {
|
|
yOffset -= yMove;
|
|
if( yOffset < 0) yOffset = 0; //move to center Y
|
|
}
|
|
|
|
if ( action == ACT_DOWN || action == ACT_FALL || action == ACT_IN_HOLE) {
|
|
var holdOnBar = 0;
|
|
if(curToken == BAR_T) {
|
|
if( yOffset < 0) holdOnBar = 1;
|
|
else if(action == ACT_DOWN && y < maxTileY && map[x][y+1].act != LADDR_T) {
|
|
action = ACT_FALL; //shape fixed: 2014/03/27
|
|
}
|
|
}
|
|
|
|
yOffset += yMove;
|
|
|
|
if(holdOnBar == 1 && yOffset >= 0) {
|
|
yOffset = 0; //fall and hold on bar
|
|
action = ACT_FALL_BAR;
|
|
}
|
|
if(stayCurrPos && yOffset > 0 ) yOffset = 0; //stay on current position
|
|
else if(yOffset > H2) { //move to y+1 position
|
|
if(curToken == BLOCK_T || curToken == HLADR_T) curToken = EMPTY_T; //in hole or hide laddr
|
|
map[x][y].act = curToken; //runner move to [x][y+1], so set [x][y].act to previous state
|
|
y++;
|
|
yOffset = yOffset - tileH;
|
|
if(map[x][y].act == RUNNER_T) setRunnerDead(); //collision
|
|
//map[x][y].act = GUARD_T;
|
|
}
|
|
|
|
//add condition: AI version >= 3 will decrease drop count while guard fall
|
|
if( ((curAiVersion >= 3 && action == ACT_FALL) || action == ACT_DOWN) &&
|
|
yOffset >= 0 && yOffset < yMove)
|
|
{ //try drop gold
|
|
dropGold(id); //decrease count
|
|
}
|
|
|
|
if(action == ACT_IN_HOLE) { //check in hole or still falling
|
|
if (yOffset < 0) {
|
|
action = ACT_FALL; //still falling
|
|
|
|
//----------------------------------------------------------------------
|
|
//For AI version >= 4, drop gold before guard failing into hole totally
|
|
if(curAiVersion >= 4 && curGuard.hasGold > 0) {
|
|
if(map[x][y-1].base == EMPTY_T) {
|
|
//drop gold above
|
|
addGold(x, y-1);
|
|
} else
|
|
decGold(); //gold disappear
|
|
curGuard.hasGold = 0;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
|
|
} else { //fall into hole (yOffset MUST = 0)
|
|
|
|
//----------------------------------------------------------------------
|
|
//For AI version < 4, drop gold after guard failing into hole totally
|
|
if( curGuard.hasGold > 0 ) {
|
|
if(map[x][y-1].base == EMPTY_T) {
|
|
//drop gold above
|
|
addGold(x, y-1);
|
|
} else
|
|
decGold(); //gold disappear
|
|
}
|
|
curGuard.hasGold = 0;
|
|
//----------------------------------------------------------------------
|
|
|
|
if( curShape == "fallRight") newShape = "shakeRight";
|
|
else newShape = "shakeLeft";
|
|
themeSoundPlay("trap");
|
|
shakeTimeStart = recordCount; //for debug
|
|
if(curAiVersion < 3) {
|
|
curGuard.sprite.on("animationend", function() { climbOut(id); });
|
|
} else {
|
|
add2GuardShakeQueue(id, newShape);
|
|
}
|
|
|
|
if(playMode == PLAY_CLASSIC || playMode == PLAY_AUTO || playMode == PLAY_DEMO) {
|
|
drawScore(SCORE_IN_HOLE);
|
|
} else {
|
|
//for modem mode & edit mode
|
|
//drawGuard(1); //only guard dead need add count
|
|
}
|
|
}
|
|
}
|
|
|
|
if(action == ACT_DOWN) {
|
|
newShape = "runUpDn";
|
|
} else { //ACT_FALL or ACT_FALL_BAR
|
|
if (action == ACT_FALL_BAR) {
|
|
if(curGuard.lastLeftRight == ACT_LEFT) newShape = "barLeft";
|
|
else newShape = "barRight";
|
|
} else {
|
|
if(action == ACT_FALL && curShape != "fallLeft" && curShape != "fallRight") {
|
|
if (curGuard.lastLeftRight == ACT_LEFT) newShape = "fallLeft";
|
|
else newShape = "fallRight";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( centerY == ACT_DOWN ) {
|
|
yOffset += yMove;
|
|
if ( yOffset > 0 ) yOffset = 0; //move to center Y
|
|
}
|
|
|
|
if ( action == ACT_LEFT) {
|
|
xOffset -= xMove;
|
|
|
|
if(stayCurrPos && xOffset < 0) xOffset = 0; //stay on current position
|
|
else if ( xOffset < -W2) { //move to x-1 position
|
|
if(curToken == BLOCK_T || curToken == HLADR_T) curToken = EMPTY_T; //in hole or hide laddr
|
|
map[x][y].act = curToken; //runner move to [x-1][y], so set [x][y].act to previous state
|
|
x--;
|
|
xOffset = tileW + xOffset;
|
|
if(map[x][y].act == RUNNER_T) setRunnerDead(); //collision
|
|
//map[x][y].act = GUARD_T;
|
|
}
|
|
if( xOffset <= 0 && xOffset > -xMove) {
|
|
dropGold(id); //try to drop gold
|
|
}
|
|
if(curToken == BAR_T) newShape = "barLeft";
|
|
else newShape = "runLeft";
|
|
}
|
|
|
|
if ( centerX == ACT_LEFT ) {
|
|
xOffset -= xMove;
|
|
if ( xOffset < 0) xOffset = 0; //move to center X
|
|
}
|
|
|
|
if ( action == ACT_RIGHT ) {
|
|
xOffset += xMove;
|
|
|
|
if(stayCurrPos && xOffset > 0) xOffset = 0; //stay on current position
|
|
else if ( xOffset > W2) { //move to x+1 position
|
|
if(curToken == BLOCK_T || curToken == HLADR_T) curToken = EMPTY_T; //in hole or hide laddr
|
|
map[x][y].act = curToken; //runner move to [x+1][y], so set [x][y].act to previous state
|
|
x++;
|
|
xOffset = xOffset - tileW;
|
|
if(map[x][y].act == RUNNER_T) setRunnerDead(); //collision
|
|
//map[x][y].act = GUARD_T;
|
|
}
|
|
if( xOffset >= 0 && xOffset < xMove) {
|
|
dropGold(id);
|
|
}
|
|
if(curToken == BAR_T) newShape = "barRight";
|
|
else newShape = "runRight";
|
|
}
|
|
|
|
if ( centerX == ACT_RIGHT ) {
|
|
xOffset += xMove;
|
|
if ( xOffset > 0) xOffset = 0; //move to center X
|
|
}
|
|
|
|
//if(curGuard == ACT_CLIMB_OUT) action == ACT_CLIMB_OUT;
|
|
|
|
if(action == ACT_STOP) {
|
|
if(curGuard.action != ACT_STOP){
|
|
curGuard.sprite.stop();
|
|
if(curGuard.action != ACT_CLIMB_OUT) curGuard.action = ACT_STOP;
|
|
}
|
|
} else {
|
|
if(curGuard.action == ACT_CLIMB_OUT) action = ACT_CLIMB_OUT;
|
|
curGuard.sprite.x = (x * tileW + xOffset) * tileScale | 0;
|
|
curGuard.sprite.y = (y * tileH + yOffset) * tileScale | 0;
|
|
curGuard.pos = { x:x, y:y, xOffset:xOffset, yOffset:yOffset};
|
|
if(curShape != newShape) {
|
|
curGuard.sprite.gotoAndPlay(newShape);
|
|
curGuard.shape = newShape;
|
|
}
|
|
if(action != curGuard.action){
|
|
curGuard.sprite.play();
|
|
}
|
|
curGuard.action = action;
|
|
if(action == ACT_LEFT || action == ACT_RIGHT) curGuard.lastLeftRight = action;
|
|
}
|
|
map[x][y].act = GUARD_T;
|
|
|
|
// Check to get gold and carry
|
|
if(map[x][y].base == GOLD_T && curGuard.hasGold == 0 &&
|
|
((!xOffset && yOffset >= 0 && yOffset < H4) ||
|
|
(!yOffset && xOffset >= 0 && xOffset < W4) ||
|
|
(y < maxTileY && map[x][y+1].base == LADDR_T && yOffset < H4) // gold above laddr
|
|
)
|
|
)
|
|
{
|
|
//curGuard.hasGold = ((Math.random()*26)+14)|0; //14 - 39
|
|
curGuard.hasGold = ((Math.random()*26)+12)|0; //12 - 37 change gold drop steps
|
|
|
|
if(playMode == PLAY_AUTO || playMode == PLAY_DEMO || playMode == PLAY_DEMO_ONCE) getDemoGold(curGuard);
|
|
if(recordMode) processRecordGold(curGuard);
|
|
removeGold(x, y);
|
|
//debug ("get, (x,y) = " + x + "," + y + ", offset = " + xOffset);
|
|
}
|
|
|
|
//check collision again
|
|
//checkCollision(runner.pos.x, runner.pos.y);
|
|
}
|
|
|
|
function dropGold(id)
|
|
{
|
|
var curGuard = guard[id];
|
|
var nextToken;
|
|
var drop = 0;
|
|
|
|
switch (true) {
|
|
case (curGuard.hasGold > 1):
|
|
curGuard.hasGold--; // count > 1, don't drop it only decrease count
|
|
//loadingTxt.text = "(" + id + ") = " + curGuard.hasGold;
|
|
break;
|
|
case (curGuard.hasGold == 1):
|
|
var x = curGuard.pos.x, y = curGuard.pos.y;
|
|
|
|
if(map[x][y].base == EMPTY_T && ( y >= maxTileY ||
|
|
((nextToken = map[x][y+1].base) == BLOCK_T ||
|
|
nextToken == SOLID_T || nextToken == LADDR_T) )
|
|
) {
|
|
addGold(x,y);
|
|
curGuard.hasGold = -1; //for record play action always use value = -1
|
|
//curGuard.hasGold = -(((Math.random()*10)+1)|0); //-1 ~ -10; //waiting time for get gold
|
|
drop = 1;
|
|
}
|
|
break;
|
|
case (curGuard.hasGold < 0):
|
|
curGuard.hasGold++; //wait, don't get gold till count = 0
|
|
//loadingTxt.text = "(" + id + ") = " + curGuard.hasGold;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//=============================================
|
|
// BEGIN NEW SHAKE METHOD for AI version >= 3
|
|
//=============================================
|
|
var DEBUG_TIME = 0;
|
|
var shakeRight = [ 8, 9, 10, 9, 10, 8 ];
|
|
var shakeLeft = [ 19, 20, 21, 20, 21, 19 ];
|
|
var shakeTime = [ 36, 3, 3, 3, 3, 3 ];
|
|
|
|
var shakingGuardList = [];
|
|
|
|
function initStillFrameVariable()
|
|
{
|
|
initGuardShakeVariable();
|
|
initFillHoleVariable();
|
|
initRebornVariable();
|
|
}
|
|
|
|
function initGuardShakeVariable()
|
|
{
|
|
shakingGuardList = [];
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Shake time extension when guard in hole,
|
|
// so the runner can dig three hold for three guards and pass through them.
|
|
// The behavior almost same as original lode runner (APPLE-II version).
|
|
// 2016/06/04
|
|
//-------------------------------------------------------------------------
|
|
if(curAiVersion <= 3) shakeTime = [ 36, 3, 3, 3, 3, 3 ]; //for AI VERSION = 3
|
|
else shakeTime = [ 51, 3, 3, 3, 3, 3 ]; //for AI VERSION > 3
|
|
}
|
|
|
|
function add2GuardShakeQueue(id, shape)
|
|
{
|
|
var curGuard = guard[id];
|
|
|
|
if(shape == "shakeRight") {
|
|
curGuard.shapeFrame = shakeRight;
|
|
} else {
|
|
curGuard.shapeFrame = shakeLeft;
|
|
}
|
|
|
|
curGuard.curFrameIdx = 0;
|
|
curGuard.curFrameTime = -1; //for init
|
|
|
|
shakingGuardList.push(id);
|
|
//error(arguments.callee.name, "push id =" + id + "(" + shakingGuardList + ")" );
|
|
|
|
}
|
|
|
|
function processGuardShake()
|
|
{
|
|
var curGuard, curIdx;
|
|
for(var i = 0; i < shakingGuardList.length;) {
|
|
curGuard = guard[shakingGuardList[i]];
|
|
curIdx = curGuard.curFrameIdx;
|
|
|
|
if( curGuard.curFrameTime < 0) { //start shake => set still frame
|
|
curGuard.curFrameTime = 0;
|
|
curGuard.sprite.gotoAndStop(curGuard.shapeFrame[curIdx]);
|
|
} else {
|
|
if(++curGuard.curFrameTime >= shakeTime[curIdx]) {
|
|
if(++curGuard.curFrameIdx < curGuard.shapeFrame.length) {
|
|
//change frame
|
|
curGuard.curFrameTime = 0;
|
|
curGuard.sprite.gotoAndStop(curGuard.shapeFrame[curGuard.curFrameIdx]);
|
|
} else {
|
|
//shake time out
|
|
|
|
var id = shakingGuardList[i];
|
|
shakingGuardList.splice(i, 1); //remove from list
|
|
//error(arguments.callee.name, "remove id =" + id + "(" + shakingGuardList + ")" );
|
|
climbOut(id); //climb out
|
|
continue;
|
|
}
|
|
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
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 + ")" );
|
|
return;
|
|
}
|
|
}
|
|
error(arguments.callee.name, "design error id =" + id + "(" + shakingGuardList + ")" );
|
|
}
|
|
|
|
//======================
|
|
// END NEW SHAKE METHOD
|
|
//======================
|
|
|
|
function climbOut(id)
|
|
{
|
|
var curGuard = guard[id]
|
|
|
|
curGuard.action = ACT_CLIMB_OUT;
|
|
curGuard.sprite.removeAllEventListeners ("animationend");
|
|
curGuard.sprite.gotoAndPlay("runUpDn");
|
|
curGuard.shape = "runUpDn";
|
|
curGuard.holePos = {x: curGuard.pos.x, y: curGuard.pos.y };
|
|
|
|
if(DEBUG_TIME) loadingTxt.text = "ShakeTime = " + (recordCount - shakeTimeStart); //for debug
|
|
}
|
|
|
|
function bestMove(id)
|
|
{
|
|
var guarder = guard[id];
|
|
var x = guarder.pos.x;
|
|
var xOffset = guarder.pos.xOffset;
|
|
var y = guarder.pos.y;
|
|
var yOffset = guarder.pos.yOffset;
|
|
|
|
var curToken, nextBelow, nextMove;
|
|
var checkSameLevelOnly = 0;
|
|
|
|
curToken = map[x][y].base;
|
|
|
|
if(guarder.action == ACT_CLIMB_OUT) { //clib from hole
|
|
if(guarder.pos.y == guarder.holePos.y) {
|
|
return (ACT_UP);
|
|
}else {
|
|
checkSameLevelOnly = 1;
|
|
if(guarder.pos.x != guarder.holePos.x) { //out of hole
|
|
guarder.action = ACT_LEFT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !checkSameLevelOnly) {
|
|
|
|
/****** next check to see if enm must fall. if so ***********/
|
|
/****** return e_fall and skip the rest. ***********/
|
|
|
|
if ( curToken == LADDR_T || (curToken == BAR_T && yOffset === 0) ) { //ladder & bar
|
|
/* no guard fall */
|
|
} else if ( yOffset < 0) //no laddr & yOffset < 0 ==> falling
|
|
return (ACT_FALL);
|
|
else if ( y < maxTileY )
|
|
{
|
|
nextBelow = map[x][y+1].act;
|
|
|
|
if ( (nextBelow == EMPTY_T || nextBelow == RUNNER_T )) {
|
|
return (ACT_FALL);
|
|
} else if ( nextBelow == BLOCK_T || nextBelow == SOLID_T ||
|
|
nextBelow == GUARD_T || nextBelow == LADDR_T ) {
|
|
/* no guard fall */
|
|
} else {
|
|
return ( ACT_FALL );
|
|
}
|
|
}
|
|
}
|
|
|
|
/******* next check to see if palyer on same floor *********/
|
|
/******* and whether enm can get him. Ignore walls *********/
|
|
var runnerX = runner.pos.x;
|
|
var runnerY = runner.pos.y;
|
|
|
|
// if ( y == runnerY ) { // same floor with runner
|
|
if ( y == runnerY && runner.action != ACT_FALL) { //case : guard on laddr and falling => don't catch it
|
|
while ( x != runnerX ) {
|
|
if ( y < maxTileY )
|
|
nextBelow = map[x][y+1].base;
|
|
else nextBelow = SOLID_T;
|
|
|
|
curToken = map[x][y].base;
|
|
|
|
if ( curToken == LADDR_T || curToken == BAR_T || // go through
|
|
nextBelow == SOLID_T || nextBelow == LADDR_T ||
|
|
nextBelow == BLOCK_T || map[x][y+1].act == GUARD_T || //fixed: must check map[].act with guard_t (for support champLevel:43)
|
|
nextBelow == BAR_T || nextBelow == GOLD_T) //add BAR_T & GOLD_T for support level 92
|
|
{
|
|
if ( x < runnerX) // guard left to runner
|
|
++x;
|
|
else if ( x > runnerX )
|
|
--x; // guard right to runner
|
|
} else break; // exit loop with closest x if no path to runner
|
|
}
|
|
|
|
if ( x == runnerX ) // scan for a path ignoring walls is a success
|
|
{
|
|
if (guarder.pos.x < runnerX ) { //if left of man go right else left
|
|
nextMove = ACT_RIGHT;
|
|
} else if ( guarder.pos.x > runnerX ) {
|
|
nextMove = ACT_LEFT;
|
|
} else { // guard X = runner X
|
|
if ( guarder.pos.xOffset < runner.pos.xOffset )
|
|
nextMove = ACT_RIGHT;
|
|
else
|
|
nextMove = ACT_LEFT;
|
|
}
|
|
return (nextMove);
|
|
}
|
|
} // if ( y == runnerY ) { ...
|
|
|
|
/********** If can't reach man on current level, then scan floor *********/
|
|
/********** (ignoring walls) and look up and down for best move *********/
|
|
|
|
return scanFloor(id);
|
|
}
|
|
|
|
var bestPath, bestRating, curRating;
|
|
var leftEnd, rightEnd;
|
|
var startX, startY;
|
|
|
|
function scanFloor(id)
|
|
{
|
|
var x, y;
|
|
var curToken, nextBelow;
|
|
var curPath;
|
|
|
|
x = startX = guard[id].pos.x;
|
|
y = startY = guard[id].pos.y;
|
|
|
|
bestRating = 255; // start with worst rating
|
|
curRating = 255;
|
|
bestPath = ACT_STOP;
|
|
|
|
/****** get ends for search along floor ******/
|
|
|
|
while ( x > 0 ) { //get left end first
|
|
curToken = map[x-1][y].act;
|
|
if ( curToken == BLOCK_T || curToken == SOLID_T )
|
|
break;
|
|
if ( curToken == LADDR_T || curToken == BAR_T || y >= maxTileY ||
|
|
y < maxTileY && ( ( nextBelow = map[x-1][y+1].base) == BLOCK_T ||
|
|
nextBelow == SOLID_T || nextBelow == LADDR_T ) )
|
|
--x;
|
|
else {
|
|
--x; // go on left anyway
|
|
break;
|
|
}
|
|
}
|
|
|
|
leftEnd = x;
|
|
x = startX;
|
|
while ( x < maxTileX ) { // get right end next
|
|
curToken = map[x+1][y].act;
|
|
if ( curToken == BLOCK_T || curToken == SOLID_T )
|
|
break;
|
|
|
|
if ( curToken == LADDR_T || curToken == BAR_T || y >= maxTileY ||
|
|
y < maxTileY && ( ( nextBelow = map[x+1][y+1].base) == BLOCK_T ||
|
|
nextBelow == SOLID_T || nextBelow == LADDR_T ) )
|
|
++x;
|
|
else { // go on right anyway
|
|
++x;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rightEnd = x;
|
|
|
|
/******* Do middle scan first for best rating and direction *******/
|
|
|
|
x = startX;
|
|
if ( y < maxTileY &&
|
|
(nextBelow = map[x][y+1].base) != BLOCK_T && nextBelow != SOLID_T )
|
|
scanDown( x, ACT_DOWN );
|
|
|
|
if( map[x][y].base == LADDR_T )
|
|
scanUp( x, ACT_UP );
|
|
|
|
/******* next scan both sides of floor for best rating *******/
|
|
|
|
curPath = ACT_LEFT;
|
|
x = leftEnd;
|
|
|
|
while ( true ) {
|
|
if ( x == startX ) {
|
|
if ( curPath == ACT_LEFT && rightEnd != startX ) {
|
|
curPath = ACT_RIGHT;
|
|
x = rightEnd;
|
|
}
|
|
else break;
|
|
}
|
|
|
|
if( y < maxTileY &&
|
|
(nextBelow = map [x][y+1].base) != BLOCK_T && nextBelow != SOLID_T )
|
|
scanDown (x, curPath );
|
|
|
|
if( map[x][y].base == LADDR_T )
|
|
scanUp ( x, curPath );
|
|
|
|
if ( curPath == ACT_LEFT )
|
|
x++;
|
|
else x--;
|
|
}
|
|
|
|
|
|
return ( bestPath );
|
|
} // end scan floor for best direction to go
|
|
|
|
function scanDown(x, curPath )
|
|
{
|
|
var y;
|
|
var nextBelow; //curRating;
|
|
var runnerX = runner.pos.x;
|
|
var runnerY = runner.pos.y;
|
|
|
|
y = startY;
|
|
|
|
while( y < maxTileY && ( nextBelow = map [x][y+1].base) != BLOCK_T &&
|
|
nextBelow != SOLID_T ) // while no floor below ==> can move down
|
|
{
|
|
if ( map[x][y].base != EMPTY_T && map[x][y].base != HLADR_T) { // if not falling ==> try move left or right
|
|
//************************************************************************************
|
|
// 2014/04/14 Add check "map[x][y].base != HLADR_T" for support
|
|
// champLevel 19: a guard in hole with h-ladder can run left after dig the left hole
|
|
//************************************************************************************
|
|
if ( x > 0 ) { // if not at left edge check left side
|
|
if ( (nextBelow = map[x-1][y+1].base) == BLOCK_T ||
|
|
nextBelow == LADDR_T || nextBelow == SOLID_T ||
|
|
map[x-1][y].base == BAR_T ) // can move left
|
|
{
|
|
if ( y >= runnerY ) // no need to go on
|
|
break; // already below runner
|
|
}
|
|
}
|
|
|
|
if ( x < maxTileX ) // if not at right edge check right side
|
|
{
|
|
if ( (nextBelow = map[x+1][y+1].base) == BLOCK_T ||
|
|
nextBelow == LADDR_T || nextBelow == SOLID_T ||
|
|
map[x+1][y].base == BAR_T ) // can move right
|
|
{
|
|
if ( y >= runnerY )
|
|
break;
|
|
}
|
|
}
|
|
} // end if not falling
|
|
++y; // go to next level down
|
|
} // end while ( y < maxTileY ... ) scan down
|
|
|
|
if( y == runnerY ) { // update best rating and direct.
|
|
curRating = Math.abs(startX - x);
|
|
// if ( (curRating = runnerX - x) < 0) //BUG from original book ? (changed by Simon)
|
|
// curRating = -curRating; //ABS
|
|
} else if ( y > runnerY )
|
|
curRating = y - runnerY + 200; // position below runner
|
|
else curRating = runnerY - y + 100; // position above runner
|
|
|
|
if( curRating < bestRating )
|
|
{
|
|
bestRating = curRating;
|
|
bestPath = curPath;
|
|
}
|
|
|
|
} // end Scan Down
|
|
|
|
function scanUp( x, curPath )
|
|
{
|
|
var y;
|
|
var nextBelow; //curRating;
|
|
var runnerX = runner.pos.x;
|
|
var runnerY = runner.pos.y;
|
|
|
|
y = startY;
|
|
|
|
while ( y > 0 && map[x][y].base == LADDR_T ) { // while can go up
|
|
--y;
|
|
if ( x > 0 ) { // if not at left edge check left side
|
|
if ( (nextBelow = map[x-1][y+1].base) == BLOCK_T ||
|
|
nextBelow == SOLID_T || nextBelow == LADDR_T ||
|
|
map[x-1][y].base == BAR_T ) // can move left
|
|
{
|
|
if ( y <= runnerY ) // no need to go on
|
|
break; // already above runner
|
|
}
|
|
}
|
|
|
|
if ( x < maxTileX ) { // if not at right edge check right side
|
|
if ( (nextBelow = map[x+1][y+1].base) == BLOCK_T ||
|
|
nextBelow == SOLID_T || nextBelow == LADDR_T ||
|
|
map[x+1][y].base == BAR_T ) // can move right
|
|
{
|
|
if ( y <= runnerY )
|
|
break;
|
|
}
|
|
}
|
|
//--y;
|
|
} // end while ( y > 0 && laddr up) scan up
|
|
|
|
if ( y == runnerY ) { // update best rating and direct.
|
|
curRating = Math.abs(startX - x);
|
|
//if ( (curRating = runnerX - x) < 0) // BUG from original book ? (changed by Simon)
|
|
// curRating = -curRating; //ABS
|
|
} else if ( y > runnerY )
|
|
curRating = y - runnerY + 200; // position below runner
|
|
else curRating = runnerY - y + 100; // position above runner
|
|
|
|
if ( curRating < bestRating )
|
|
{
|
|
bestRating = curRating;
|
|
bestPath = curPath;
|
|
}
|
|
|
|
} // end scan Up
|
|
|
|
var bornRndX; //range random 0..maxTileX
|
|
|
|
function initRnd()
|
|
{
|
|
//bornRndX = new rangeRandom(0, maxTileX, curLevel); //set fixed seed for demo mode
|
|
bornRndX = new rangeRandom(0, maxTileX, 0); //random range 0 .. maxTileX
|
|
}
|
|
|
|
function getGuardId(x, y)
|
|
{
|
|
var id;
|
|
|
|
for(id = 0; id < guardCount; id++) {
|
|
if ( guard[id].pos.x == x && guard[id].pos.y == y) break;
|
|
}
|
|
assert(id < guardCount, "Error: can not get guard position!");
|
|
|
|
return id;
|
|
}
|
|
|
|
function guardReborn(x, y)
|
|
{
|
|
var id;
|
|
|
|
//get guard id by current in hole position
|
|
id = getGuardId(x, y);
|
|
|
|
var bornY = 1; //start on line 2
|
|
var bornX = bornRndX.get();
|
|
var rndStart = bornX;
|
|
|
|
|
|
while(map[bornX][bornY].act != EMPTY_T || map[bornX][bornY].base == GOLD_T || map[bornX][bornY].base == BLOCK_T ) {
|
|
//BUG FIXED for level 115 (can not reborn at bornX=27)
|
|
//don't born at gold position & diged position, 2/24/2015
|
|
if( (bornX = bornRndX.get()) == rndStart) {
|
|
bornY++;
|
|
}
|
|
assert(bornY <= maxTileY, "Error: Born Y too large !");
|
|
}
|
|
//debug("bornX = " + bornX);
|
|
if(playMode == PLAY_AUTO || playMode == PLAY_DEMO || playMode == PLAY_DEMO_ONCE) {
|
|
var bornPos = getDemoBornPos();
|
|
bornX = bornPos.x;
|
|
bornY = bornPos.y;
|
|
}
|
|
|
|
if(recordMode == RECORD_KEY) saveRecordBornPos(bornX, bornY);
|
|
else if(recordMode == RECORD_PLAY) {
|
|
var bornPos = getRecordBornPos();
|
|
bornX = bornPos.x;
|
|
bornY = bornPos.y;
|
|
}
|
|
|
|
map[bornX][bornY].act = GUARD_T;
|
|
//debug("born (x,y) = (" + bornX + "," + bornY + ")");
|
|
|
|
var curGuard = guard[id];
|
|
|
|
curGuard.pos = { x:bornX, y:bornY, xOffset:0, yOffset: 0 };
|
|
curGuard.sprite.x = bornX * tileWScale | 0;
|
|
curGuard.sprite.y = bornY * tileHScale | 0;
|
|
|
|
rebornTimeStart = recordCount;
|
|
if(curAiVersion < 3) {
|
|
curGuard.sprite.on("animationend", function() { rebornComplete(id); });
|
|
curGuard.sprite.gotoAndPlay("reborn");
|
|
} else {
|
|
add2RebornQueue(id);
|
|
}
|
|
|
|
curGuard.shape = "reborn";
|
|
curGuard.action = ACT_REBORN;
|
|
|
|
}
|
|
|
|
function rebornComplete(id)
|
|
{
|
|
var x = guard[id].pos.x;
|
|
var y = guard[id].pos.y;
|
|
|
|
if( map[x][y].act == RUNNER_T) setRunnerDead(); //collision
|
|
map[x][y].act = GUARD_T;
|
|
guard[id].sprite.removeAllEventListeners ("animationend");
|
|
guard[id].action = ACT_FALL;
|
|
guard[id].shape = "fallRight";
|
|
//guard[id].hasGold = 0;
|
|
guard[id].sprite.gotoAndPlay("fallRight");
|
|
themeSoundPlay("reborn");
|
|
|
|
if(DEBUG_TIME) loadingTxt.text = "rebornTime = " + (recordCount - rebornTimeStart); //for debug
|
|
}
|
|
|
|
function setRunnerDead()
|
|
{
|
|
if(!godMode) gameState = GAME_RUNNER_DEAD;
|
|
}
|
|
|
|
//===============================================
|
|
// BEGIN NEW FOR REBORN (ai version >= 3)
|
|
//===============================================
|
|
var rebornFrame = [ 17, 18 ];
|
|
var rebornTime = [ 6, 2 ];
|
|
|
|
var rebornGuardList = [];
|
|
|
|
function initRebornVariable()
|
|
{
|
|
rebornGuardList = [];
|
|
}
|
|
|
|
function add2RebornQueue(id)
|
|
{
|
|
var curGuard = guard[id];
|
|
|
|
curGuard.sprite.gotoAndStop("reborn");
|
|
curGuard.curFrameIdx = 0;
|
|
curGuard.curFrameTime = -1;
|
|
|
|
rebornGuardList.push(id);
|
|
}
|
|
|
|
function processReborn()
|
|
{
|
|
var curGuard, curIdx;
|
|
|
|
for(var i = 0; i < rebornGuardList.length;) {
|
|
curGuard = guard[rebornGuardList[i]];
|
|
curIdx = curGuard.curFrameIdx;
|
|
|
|
if(++curGuard.curFrameTime >= rebornTime[curIdx]) {
|
|
if(++curGuard.curFrameIdx < rebornFrame.length) {
|
|
//change frame
|
|
curGuard.curFrameTime = 0;
|
|
curGuard.sprite.gotoAndStop(rebornFrame[curGuard.curFrameIdx]);
|
|
} else {
|
|
//reborn
|
|
|
|
var id = rebornGuardList[i];
|
|
rebornGuardList.splice(i, 1); //remove from list
|
|
rebornComplete(id);
|
|
continue;
|
|
}
|
|
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
//====================
|
|
// END NEW FOR REBORN
|
|
//====================
|