Fichiers
simon_hung 24a7cc1749 Lode Runner v2.30e, AI_VERSION=4
The major change is adding a backup and restore feature for "custom levels".
2025-03-26 22:30:17 +08:00

974 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;
guardRemoveRedhat(curGuard); //9/4/2016
}
//----------------------------------------------------------------------
} 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;
guardRemoveRedhat(curGuard); //9/4/2016
//----------------------------------------------------------------------
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
guardWearRedhat(curGuard); //9/4/2016
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);
}
//meanings: guard with gold
function guardWearRedhat(guard)
{
if(redhatMode) guard.sprite.spriteSheet = redhatData;
}
//meanings: guard without gold
function guardRemoveRedhat(guard)
{
if(redhatMode) guard.sprite.spriteSheet = guardData;
}
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): //drop gold
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
guardRemoveRedhat(curGuard); //9/4/2016
drop = 1;
}
break;
case (curGuard.hasGold < 0):
curGuard.hasGold++; //wait, don't get gold till count = 0
//loadingTxt.text = "(" + id + ") = " + curGuard.hasGold;
break;
}
return drop;
}
//=============================================
// BEGIN NEW SHAKE METHOD for AI version >= 3
//=============================================
var DEBUG_TIME = 0;
var shakeRight = [ 8, 9, 10, 9, 10, 8 ];
var shakeLeft = [ 30, 31, 32, 31, 32, 30 ];
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("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("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("remove id =" + id + "(" + shakingGuardList + ")" );
return;
}
}
error("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 = [ 28, 29 ];
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
//====================