Fichiers
LodeRunner_TotalRecall/lodeRunner.runner.js
T
2014-06-20 16:33:36 +08:00

628 lignes
16 KiB
JavaScript

//=============================================================================
// AI reference book: "玩 Lode Runner 學 C 語言"
// http://www.kingstone.com.tw/book/book_page.asp?kmcode=2014710650538
//=============================================================================
var STATE_OK_TO_MOVE = 1, STATE_FALLING = 2;
function moveRunner()
{
var x = runner.pos.x;
var xOffset = runner.pos.xOffset;
var y = runner.pos.y;
var yOffset = runner.pos.yOffset;
var curState;
var curToken, nextToken;
curToken = map[x][y].base;
if ( curToken == LADDR_T || (curToken == BAR_T && yOffset == 0) ) { //ladder & bar
curState = STATE_OK_TO_MOVE; //ok to move (on ladder or bar)
} else if (yOffset < 0) { //no ladder && yOffset < 0 ==> falling
curState = STATE_FALLING;
} else if (y < maxTileY) { //no laddr && y < maxTileY && yOffset >= 0
nextToken = map[x][y+1].act;
switch (true) {
case (nextToken == EMPTY_T):
curState = STATE_FALLING;
break;
case ( nextToken == BLOCK_T || nextToken == LADDR_T || nextToken == SOLID_T):
curState = STATE_OK_TO_MOVE;
break;
case ( nextToken == GUARD_T):
curState = STATE_OK_TO_MOVE;
break;
default:
curState = STATE_FALLING;
break;
}
} else { // no laddr && y == maxTileY
curState = STATE_OK_TO_MOVE;
}
if ( curState == STATE_FALLING ) {
stayCurrPos = ( y >= maxTileY ||
(nextToken = map[x][y+1].act) == BLOCK_T ||
nextToken == SOLID_T || nextToken == GUARD_T);
runnerMoveStep(ACT_FALL, stayCurrPos);
return;
}
/****** Check Key Action ******/
var moveStep = ACT_STOP;
var stayCurrPos = 1;
switch(keyAction) {
case ACT_UP:
stayCurrPos = ( y <= 0 ||
( nextToken = map[x][y-1].act) == BLOCK_T ||
nextToken == SOLID_T || nextToken == TRAP_T );
if (y > 0 && map[x][y].base != LADDR_T && yOffset < H4 && yOffset > 0 && map[x][y+1].base == LADDR_T)
{
stayCurrPos = 1;
moveStep = ACT_UP;
} else
if (!( map[x][y].base != LADDR_T &&
(yOffset <= 0 || map[x][y+1].base != LADDR_T) ||
(yOffset <= 0 && stayCurrPos ) )
) {
moveStep = ACT_UP;
}
break;
case ACT_DOWN:
stayCurrPos = ( y >= maxTileY ||
(nextToken = map[x][y+1].act) == BLOCK_T ||
nextToken == SOLID_T);
if (!(yOffset >= 0 && stayCurrPos))
moveStep = ACT_DOWN;
break;
case ACT_LEFT:
stayCurrPos = ( x <= 0 ||
(nextToken = map[x-1][y].act) == BLOCK_T ||
nextToken == SOLID_T || nextToken == TRAP_T);
if (!(xOffset <= 0 && stayCurrPos))
moveStep = ACT_LEFT;
break;
case ACT_RIGHT:
stayCurrPos = ( x >= maxTileX ||
(nextToken = map[x+1][y].act) == BLOCK_T ||
nextToken == SOLID_T || nextToken == TRAP_T);
if (!(xOffset >= 0 && stayCurrPos))
moveStep = ACT_RIGHT;
break;
case ACT_DIG_LEFT:
case ACT_DIG_RIGHT:
if(ok2Dig(keyAction)) {
runnerMoveStep(keyAction, stayCurrPos);
digHole(keyAction);
} else {
runnerMoveStep(ACT_STOP, stayCurrPos);
}
keyAction = ACT_STOP;
return;
}
runnerMoveStep(moveStep, stayCurrPos);
}
function runnerMoveStep(action, stayCurrPos )
{
var x = runner.pos.x;
var xOffset = runner.pos.xOffset;
var y = runner.pos.y;
var yOffset = runner.pos.yOffset;
var curToken, nextToken, centerX, centerY;
var curShape, newShape;
curShape = newShape = runner.shape;
centerX = centerY = ACT_STOP;
switch(action) {
case ACT_DIG_LEFT:
case ACT_DIG_RIGHT:
xOffset = 0;
yOffset = 0;
break;
case ACT_UP:
case ACT_DOWN:
case ACT_FALL:
if ( xOffset > 0 ) centerX = ACT_LEFT;
else if (xOffset < 0) centerX = ACT_RIGHT;
break;
case ACT_LEFT:
case ACT_RIGHT:
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 == GUARD_T) gameState = GAME_RUNNER_DEAD; //collision
}
newShape = "runUpDn";
}
if ( centerY == ACT_UP ) {
yOffset -= yMove;
if( yOffset < 0) yOffset = 0; //move to center Y
}
if ( action == ACT_DOWN || action == ACT_FALL) {
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 == GUARD_T) gameState = GAME_RUNNER_DEAD; //collision
}
if(action == ACT_DOWN) {
newShape = "runUpDn";
} else { //ACT_FALL or ACT_FALL_BAR
if (y < maxTileY && map[x][y+1].act == GUARD_T) { //over guard
//don't collision
var id = getGuardId(x, y+1);
if(yOffset > guard[id].pos.yOffset) yOffset = guard[id].pos.yOffset;
}
if (action == ACT_FALL_BAR) {
if(runner.lastLeftRight == ACT_LEFT) newShape = "barLeft";
else newShape = "barRight";
} else {
if(runner.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 == GUARD_T) gameState = GAME_RUNNER_DEAD; //collision
}
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 == GUARD_T) gameState = GAME_RUNNER_DEAD; //collision
}
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(action == ACT_STOP ) {
if(runner.action == ACT_FALL) {
soundStop(soundFall);
soundPlay("down");
}
if(runner.action != ACT_STOP){
runner.sprite.stop();
runner.action = ACT_STOP;
}
} else {
runner.sprite.x = x * tileW + xOffset;
runner.sprite.y = y * tileH + yOffset;
runner.pos = { x:x, y:y, xOffset:xOffset, yOffset:yOffset};
if(curShape != newShape) {
runner.sprite.gotoAndPlay(newShape);
runner.shape = newShape;
}
if(action != runner.action){
if(runner.action == ACT_FALL) {
soundStop(soundFall);
soundPlay("down");
} else if ( action == ACT_FALL) {
soundPlay(soundFall);
}
runner.sprite.play();
}
if(action == ACT_LEFT || action == ACT_RIGHT) runner.lastLeftRight = action;
runner.action = action;
}
map[x][y].act = RUNNER_T;
// Check runner to get gold (MAX MOVE MUST < H4 & W4)
if( map[x][y].base == GOLD_T &&
((!xOffset && yOffset >= 0 && yOffset < H4) ||
(!yOffset && xOffset >= 0 && xOffset < W4) ||
(y < maxTileY && map[x][y+1].base == LADDR_T && yOffset < H4) // gold above laddr
)
)
{
removeGold(x,y);
soundPlay("getGold");
decGold();
//debug("gold = " + goldCount);
if(playMode == PLAY_CLASSIC || playMode == PLAY_AUTO || playMode == PLAY_DEMO) {
drawScore(SCORE_GET_GOLD);
} else {
//for modern mode , edit mode
drawGold(1); //get gold
}
}
//if(!goldCount && !goldComplete) showHideLaddr();
//check collision with guard !
checkCollision(x, y);
}
//dec gold count
function decGold()
{
if(--goldCount <= 0) {
//if(!showHideLaddr() && runner.pos.y > 0) {
showHideLaddr();
if(runner.pos.y > 0 && playMode != PLAY_CLASSIC && playMode != PLAY_AUTO && playMode != PLAY_DEMO) {
soundPlay("goldFinish");
}
}
}
function removeGold(x,y)
{
map[x][y].base = EMPTY_T;
mainStage.removeChild(map[x][y].bitmap);
map[x][y].bitmap = null;
}
function addGold(x, y)
{
var tile;
map[x][y].base = GOLD_T;
tile = map[x][y].bitmap = new createjs.Bitmap(preload.getResult("gold"));
tile.setTransform(x * tileW, y * tileH,tileScale, tileScale); //x,y, scaleX, scaleY
mainStage.addChild(tile);
moveSprite2Top(); //reset runner, guard & fill hole object order
}
function showHideLaddr()
{
var haveHLadder = 0;
for(var y = 0; y < NO_OF_TILES_Y; y++) {
for(var x = 0; x < NO_OF_TILES_X; x++) {
if( map[x][y].base == HLADR_T) {
haveHLadder = 1;
map[x][y].base =LADDR_T;
map[x][y].act =LADDR_T;
map[x][y].bitmap.set({alpha:1}); //display laddr
}
}
}
goldComplete = 1;
return haveHLadder;
}
function checkCollision(runnerX, runnerY)
{
var x = -1, y = -1;
//var dbg = "NO";
switch(true) {
case ( runnerY > 0 && map[runnerX][runnerY-1].act == GUARD_T):
x = runnerX; y = runnerY-1;
//dbg = "UP";
break;
case ( runnerY < maxTileY && map[runnerX][runnerY+1].act == GUARD_T):
x = runnerX; y = runnerY+1;
//dbg = "DN";
break;
case ( runnerX > 0 && map[runnerX-1][runnerY].act == GUARD_T):
x = runnerX-1; y = runnerY;
//dbg = "LF";
break;
case ( runnerX < maxTileX && map[runnerX+1][runnerY].act== GUARD_T):
x = runnerX+1; y = runnerY;
//dbg = "RT";
break;
}
//if( dbg != "NO") debug(dbg);
if( x >= 0) {
for(var i = 0; i < guardCount; i++) {
if( guard[i].pos.x == x && guard[i].pos.y == y) break;
}
assert( (i < guardCount), "checkCollision design error !");
if(guard[i].action != ACT_REBORN) {
var dw = Math.abs(runner.sprite.x - guard[i].sprite.x);
var dh = Math.abs(runner.sprite.y - guard[i].sprite.y);
//debug (" dw = " + dw +"(" + tileW + ") dh = " + dh + "(" + tileH + ")" );
/*
debug("runner:" + runner.pos.x + ","
+ runner.pos.y + ","
+ "guard:" + guard[i].pos.x + ","
+ guard[i].pos.y + ","
+ "runner:" + runner.pos.xOffset + ","
+ runner.pos.yOffset + ","
+ "guard:" + guard[i].pos.xOffset + ","
+ guard[i].pos.yOffset
);
*/
/*
if( Math.abs(runner.sprite.x - guard[i].sprite.x) < tileW &&
Math.abs(runner.sprite.y - guard[i].sprite.y) < tileH)
*/
if( Math.abs(runner.sprite.x - guard[i].sprite.x) <= W4*3 &&
Math.abs(runner.sprite.y - guard[i].sprite.y) <= H4*3 )
{
gameState = GAME_RUNNER_DEAD;
//debug("runner dead!");
}
}
}
}
//Page 276 misc.c (book)
function ok2Dig(nextMove)
{
var x = runner.pos.x;
var y = runner.pos.y;
var token, rc = 0;
switch(nextMove) {
case ACT_DIG_LEFT:
// debug("[x-1][y+1] = " + map[x-1][y+1].act + " [x-1][y] = " + map[x-1][y].act +
// "[x-1][y].base = " + map[x-1][y].base );
if( y < maxTileY && x > 0 && map[x-1][y+1].act == BLOCK_T &&
map[x-1][y].act == EMPTY_T && map[x-1][y].base != GOLD_T)
rc = 1;
break;
case ACT_DIG_RIGHT:
// debug("[x+1][y+1] = " + map[x+1][y+1].act + " [x+1][y] = " + map[x+1][y].act +
// "[x+1][y].base = " + map[x+1][y].base );
if( y < maxTileY && x < maxTileX && map[x+1][y+1].act == BLOCK_T &&
map[x+1][y].act == EMPTY_T && map[x+1][y].base != GOLD_T)
rc = 1;
break;
}
return rc;
}
function digHole(action)
{
var x,y, holeShape;
if(action == ACT_DIG_LEFT) {
x = runner.pos.x-1;
y = runner.pos.y;
runner.shape = "digLeft";
holeShape = "digHoleLeft";
} else { //DIG RIGHT
x = runner.pos.x+1;
y = runner.pos.y;
runner.shape = "digRight";
holeShape = "digHoleRight";
}
soundPlay(soundDig);
map[x][y+1].bitmap.set({alpha:0}); //hide block (replace with digging image)
runner.sprite.gotoAndPlay(runner.shape);
holeObj.sprite.gotoAndPlay(holeShape);
holeObj.action = ACT_DIGGING;
holeObj.pos = { x: x, y: y };
holeObj.sprite.setTransform(x * tileW, y * tileH,tileScale, tileScale);
// the callback is called each time a sequence completes:
holeObj.sprite.on("animationend", digComplete);
mainStage.addChild(holeObj.sprite);
}
function isDigging()
{
var rc = 0;
if(holeObj.action == ACT_DIGGING) {
var x = holeObj.pos.x, y = holeObj.pos.y;
if(map[x][y].act == GUARD_T) { //guard come close to the digging hole !
var id = getGuardId(x, y);
if(holeObj.sprite.currentAnimationFrame < 6 && guard[id].pos.yOffset > -H4) {
stopDigging(x,y);
}
} else {
switch( runner.shape ) {
case "digLeft":
if(holeObj.sprite.currentAnimationFrame > 2 ) {
runner.sprite.gotoAndStop("runLeft");
runner.shape = "runLeft";
runner.action = ACT_STOP;
}
break;
case "digRight":
if(holeObj.sprite.currentAnimationFrame > 2) {
runner.sprite.gotoAndStop("runRight");
runner.shape = "runRight";
runner.action = ACT_STOP;
}
break;
}
rc = 1;
}
}
return rc;
}
function stopDigging(x,y)
{
//(1) remove holeObj
holeObj.sprite.removeAllEventListeners ("animationend");
holeObj.action = ACT_STOP; //no digging
mainStage.removeChild(holeObj.sprite);
//(2) fill hole
y++;
map[x][y].act = map[x][y].base; //BLOCK_T
assert(map[x][y].base == BLOCK_T, "fill hole != BLOCK_T");
map[x][y].bitmap.set({alpha:1}); //display block
//(3) change runner shape
switch( runner.shape ) {
case "digLeft":
runner.sprite.gotoAndStop("runLeft");
runner.shape = "runLeft";
runner.action = ACT_STOP;
break;
case "digRight":
runner.sprite.gotoAndStop("runRight");
runner.shape = "runRight";
runner.action = ACT_STOP;
break;
}
soundStop(soundDig); //stop sound of digging
}
function digComplete()
{
var x = holeObj.pos.x;
var y = holeObj.pos.y + 1;
map[x][y].act = EMPTY_T;
holeObj.sprite.removeAllEventListeners ("animationend");
holeObj.action = ACT_STOP; //no digging
mainStage.removeChild(holeObj.sprite);
fillHole(x, y);
}
var fillHoleObj = [];
function fillHole(x, y)
{
var fillSprite = new createjs.Sprite(holeData, "fillHole");
fillSprite.setTransform(x * tileW, y * tileH,tileScale, tileScale);
fillSprite.on("animationend", fillComplete);
fillSprite.play();
mainStage.addChild(fillSprite);
fillHoleObj.push(fillSprite);
}
function moveFillHoleObj2Top()
{
for(var i = 0; i < fillHoleObj.length; i++) {
moveChild2Top(mainStage, fillHoleObj[i]);
}
}
function fillComplete()
{
var x = this.x / tileW | 0; //this : scope default to the dispatcher
var y = this.y / tileH | 0;
map[x][y].bitmap.set({alpha:1}); //display block
this.removeAllEventListeners ("animationend");
mainStage.removeChild(this);
removeFillHoleObj(this);
switch(map[x][y].act) {
case RUNNER_T : // runner dead
//loadingTxt.text = "RUNNER DEAD";
gameState = GAME_RUNNER_DEAD;
runner.sprite.set({alpha:0}); //hidden runner --> dead
break;
case GUARD_T:
var id = getGuardId(x,y);
if(guard[id].hasGold > 0) { //guard has gold and not fall into the hole
decGold();
guard[id].hasGold = 0;
}
guardReborn(x,y);
if(playMode == PLAY_CLASSIC || playMode == PLAY_AUTO || playMode == PLAY_DEMO) {
drawScore(SCORE_GUARD_DEAD);
} else {
//for modern mode & edit mode
drawGuard(1); //guard dead, add count
}
break;
}
map[x][y].act = BLOCK_T;
}
function removeFillHoleObj(spriteObj)
{
for(var i = 0; i < fillHoleObj.length; i++) {
if(fillHoleObj[i] == spriteObj) {
fillHoleObj.splice(i,1);
return;
}
}
debug("removeFillHoleObj: design error!");
}