playship_ludo_pseudo_quick
Version:
Server side neutrino plugin code for ludo game
853 lines (805 loc) • 32.8 kB
JavaScript
const enums = require('../utils/enums');
const logger = require('../utils/playshipUtils').getLogger();
const board2Player = require('./board2Player');
const board3Player = require('./board3Player');
const board4Player = require('./board4Player');
const stringify = require('json-stringify');
const eventObjs = require('../utils/newGameEvent')
const externalEventObjs = require('../utils/externalGameEvent')
const ShuffleRoller = require('../utils/shuffleRoller');
const {_getGameProp, _setGameProp, _setPlayerProp, _getPlayerProp, _gameObjById,
_getMeta,_getAllPlayerOwnedObjectIds ,_getNextTurnPlayerId,_handleRoll,
_aggregatePlayerTurnTime , _findAbsoluteWinner , _offExtraTimeIfEnabled,
_isPauseEnabled
} = require('../utils/utils');
const GAME_PROPS = enums.GAME_PROPS;
const PLAYER_PROPS = enums.PLAYER_PROPS;
const OBJECT_PROPS = enums.OBJECT_PROPS;
const gameAction = function(game, playerId, actionMessage) {
const gk = {key: game.getId()};
const turnEvent = new eventObjs.turnEvent()
if (game.getState() >= 30)
return logger.warn(gk, 'Game has ended, nothing to do');
if (game.getCurrentTurnPlayerId() !== playerId)
return logger.warn(gk, 'Received action message out of turn by', playerId);
if (_getGameProp(game, GAME_PROPS.GAME_OVER_TS) > 0)
return logger.warn(gk, 'Game is marked to end, no input allowed');
if (_getGameProp(game, GAME_PROPS.IS_TURN_PROCESSED_SERVER))
return logger.debug(gk,
'Already processed for this turn, waiting for turn over');
let tes = testableInput(game, playerId, actionMessage);
if (tes === -1)
return logger.warn(gk, 'Testable input not allowed in this environment');
if (tes === 1) return; // already processed return
game.incrPluginStats('ludo.gameaction.'+actionMessage.actionName);
if (actionMessage.actionName === 'rolledDice') {
const diceRollEvent = new eventObjs.diceRoll()
_setGameProp(game, GAME_PROPS.IS_DICE_REVEALED, true);
_handleRoll(game,playerId,actionMessage.actionSpecificData,diceRollEvent)
game.syncGameStateServVars();
return;
}
if(_getPlayerProp(game,playerId,PLAYER_PROPS.TURN_SKIP_DIE_ROLL_SIX)) return logger.info(
gk,"player %s got three consecutive 6's hence breaking",playerId
)
if (actionMessage.uniqueActionId !== 1 || actionMessage.actionName !==
'selectedPawn')
return logger.warn(gk, 'Unknown invoke action received');
let pieceId = actionMessage.actionSpecificData;
let pieceOwnerId = game.getOwner(pieceId);
if (pieceOwnerId !== playerId) {
game.incrPluginStats('ludo.foulmove.count');
return logger.warn(gk, 'Piece is not owned by player, cant move');
}
logger.debug(gk, 'Received user action piece', pieceId);
_setGameProp(game, GAME_PROPS.IS_TURN_PROCESSED, true);
_setGameProp(game, GAME_PROPS.IS_TURN_PROCESSED_SERVER, true);
game.syncGameStateServVars();
if(_isPauseEnabled(game)) _offExtraTimeIfEnabled(game,playerId,turnEvent);
turnEndHandler(game, playerId, pieceId,null ,null ,turnEvent);
};
function isAtleastOnePawnSelectable(game,playerId){
const gk = {key: game.getId()};
let pieceType = _getPlayerProp(game,playerId,PLAYER_PROPS.COLOR)
let objIds = game.getAllObjIds();
for (let objId of objIds) {
let obj = _gameObjById(game, objId)
if(pieceType === obj.type)
if (obj._get(OBJECT_PROPS.CAN_SELECT))
return true
}
logger.debug(gk, 'no moves possible');
game.sendTip(playerId, 18);
return false
}
function testableInput(game, playerId, actionMessage) {
if (actionMessage.uniqueActionId !== 100) return 0;
const gk = {key: game.getId()};
if (actionMessage.uniqueActionId === 100 && parseInt(process.env._env) === 0)
return -1;
_setGameProp(game, GAME_PROPS.IS_TURN_PROCESSED, true);
_setGameProp(game, GAME_PROPS.IS_TURN_PROCESSED_SERVER, true);
game.syncGameStateServVars();
let data = JSON.parse(actionMessage.actionSpecificData);
let pieceId = data.networkId;
let roll = data.roll;
logger.debug(gk, 'Received user test action piece', pieceId, 'roll', roll);
turnEndHandler(game, playerId, pieceId, roll);
return 1;
}
const turnEndHandler = function(game, playerId, pieceId, roll,isImmediate,
turnEventObj, addOnTime) {
const gk = {key: game.getId()};
let exec = (isTurnContinue) => {
const meta = _getMeta(game);
if (isTurnContinue === -1) {
_setGameProp(game, GAME_PROPS.IS_TURN_PROCESSED, false);
_setGameProp(game,GAME_PROPS.IS_TURN_PROCESSED_SERVER,false)
game.addTransient(GAME_PROPS.IS_SKIP_CHECKED, false);
if(turnEventObj) {
turnEventObj.kingdom = _getPlayerProp(game, playerId,
PLAYER_PROPS.TURN_COUNT)
game.publishNewGameEvent(playerId, turnEventObj)
}
game.addTransient();
game.syncPlayerServVars(playerId);
return game.syncGameStateServVars();
}
let curTurnCount = _getPlayerProp(game, playerId, PLAYER_PROPS.TURN_COUNT);
if(isTurnContinue !== 1) {
_setPlayerProp(game, playerId, PLAYER_PROPS.TURN_COUNT, curTurnCount + 1);
game.publishGameEvent(1612,[playerId],curTurnCount + 1)
}
if(turnEventObj) {
turnEventObj.score = game.getPlayerScore(playerId)
turnEventObj.kingdom = curTurnCount + 1
game.publishNewGameEvent(playerId, turnEventObj)
}
_aggregatePlayerTurnTime(game,playerId)
_setGameProp(game, GAME_PROPS.IS_CONTINUE, isTurnContinue);
let nextTurnTs = Date.now()
let extraTime = 0
if(!isImmediate) extraTime= (meta.animTime || 1500) +
(game.getTransient(GAME_PROPS.IS_EXTRA_ANIM_TIME_REQ) ?
meta.extraAnimTime : 0)
else extraTime += addOnTime ? addOnTime :0;
logger.debug({key: game.getId()}, 'extra time ', extraTime);
nextTurnTs += extraTime
_setGameProp(game, GAME_PROPS.NEXT_TURN_TS, nextTurnTs);
_setGameProp(game, GAME_PROPS.IS_TURN_PROCESSED, false);
turnInit(game, playerId,isTurnContinue);
game.syncGameStateServVars();
game.syncPlayerServVars(playerId);
logger.debug({key: game.getId()}, 'next turn ts is ', nextTurnTs);
game.incrPluginStats('ludo.turnend.count');
};
if (pieceId)
processPiece(game, playerId, pieceId, roll, turnEventObj).
then(exec).
catch(err => {
logger.error({key: game.getId()}, 'error processing turn', err);
return game.abandonGame(enums.SERVER_ERROR_REASON);
});
else exec(0);
};
const turnInit = function(game, playerId,isTurnContinue) {
const gk = {key: game.getId()};
const meta = _getMeta(game);
if (!playerId) return; // if no playerId supplied, (game start scenario)
// add new roll of die to current rolls
_setGameProp(game, GAME_PROPS.IS_DICE_REVEALED, false);
_setGameProp(game, GAME_PROPS.NO_POSSIBLE_MOVE, true);
game.addTransient(GAME_PROPS.IS_EXTRA_ANIM_TIME_REQ, false);
if(isTurnContinue) {
let newRoll = rollADie(game, playerId);
let isRolledSix = newRoll === 6 ? 1 : 0;
if(isRolledSix){
if(_getPlayerProp(game,playerId,
PLAYER_PROPS.CONSECUTIVE_SIXES) === 2){
game.incrPluginStats('ludo.consecutive.six.count');
_setPlayerProp(game,playerId,PLAYER_PROPS.TURN_SKIP_DIE_ROLL_SIX, true)
}
}
_setPlayerProp(game, playerId, PLAYER_PROPS.DIE_ROLL, newRoll);
game.syncPlayerServVars(playerId);
enablePawns(game,playerId).catch(e =>{
logger.error(gk,"Enabling Pawns failed ,", e)
});
sendTurnInitHHEvent(game,playerId,newRoll)
}
else {
_setPlayerProp(game,playerId,PLAYER_PROPS.TURN_SKIP_DIE_ROLL_SIX, false)
_setPlayerProp(game, playerId, PLAYER_PROPS.DIE_ROLL, null);
let nextTurnPlayerId = game.fetchNextTurnPlayerId()
let newRoll = rollADie(game, nextTurnPlayerId);
_setPlayerProp(game,nextTurnPlayerId,PLAYER_PROPS.DIE_ROLL, newRoll)
game.syncPlayerServVars(nextTurnPlayerId);
enablePawns(game,nextTurnPlayerId).catch(e =>{
logger.error(gk,"Enabling Pawns failed ,", e)
});
disablePawns(game,playerId);
sendTurnInitHHEvent(game,nextTurnPlayerId,newRoll)
_setGameProp(game, GAME_PROPS.IS_TURN_COMPLETED, true)
}
};
const issueTurnStartSavePoint = function(game) {
const gameKey = game.getId();
game.issueSavepoint('__turn_start', err => {
if (err) return logger.error({err: err, key: gameKey},
'Error in issuing savepoint');
logger.debug({key: gameKey}, 'pre-turn save point issued');
});
};
function getBord(game){
let playerCount = parseInt(game.getNumberOfPlayers());
switch (playerCount){
case 2 : return board2Player;
case 3 : return board3Player;
case 4 : return board4Player;
}
}
async function processPiece(game, playerId, pieceId, roll, turnEventObj) {
const gk = {key: game.getId()};
let currentBoard = getBord(game)
let scoreObj = {
score : 0,
message : {
dieRoll: 0,
extra: 0
}
}
let turnExpiry = game.getCurrentTurnExpiry();
let now = Date.now()
const meta = _getMeta(game);
let duration = meta.turnDuration;
let timeTakenToPlay = duration - turnExpiry + now;
turnEventObj.family = pieceId
turnEventObj.time_stats = timeTakenToPlay
// fetch die roll info
let dieRoll = roll || parseInt(
_getPlayerProp(game, playerId, PLAYER_PROPS.DIE_ROLL));
turnEventObj.class = dieRoll
scoreObj.score += dieRoll
scoreObj.message.dieRoll = dieRoll
let isRolledSix = dieRoll === 6 ? 1 : 0;
if(isRolledSix) {
game.addTransient(GAME_PROPS.IS_EXTRA_ANIM_TIME_REQ, true);
let consecutiveSixesCount = _getPlayerProp(game,playerId,
PLAYER_PROPS.CONSECUTIVE_SIXES) +1
_setPlayerProp(game,playerId,PLAYER_PROPS.CONSECUTIVE_SIXES,
consecutiveSixesCount)
}
logger.info(gk,"Dice roll is % , hence turn continue is ,",dieRoll, isRolledSix)
game.publishGameEvent(1608,[playerId],dieRoll)
let piece = _gameObjById(game, pieceId);
// fetch destination for current roll and piece
piece._set(OBJECT_PROPS.SCORE,piece._get(OBJECT_PROPS.SCORE) + dieRoll)
game.syncGameObjServVars(...[pieceId]);
promisifyFetchBoundaryForObjId(game);
promisifyCollidedObjsWithBoundary(game);
let curPosKey = await game.fetchBoundaryForObjIdProm(pieceId);
turnEventObj.start_value = parseInt(curPosKey)
//let curPosInfo = board.posInfoForPosKey(curPosKey);
//if (curPosInfo.isHeaven) return null;
let dest = currentBoard.nextDestination(piece.type, curPosKey, dieRoll,game,playerId);
logger.info(gk, 'Moving piece', pieceId, 'distance', dieRoll,
'current posKey', curPosKey, 'to destination', stringify(dest));
// if invalid move, dont process
if (dest == null) {
logger.warn(gk, 'Invalid piece selected to move by', playerId, 'pieceId',
pieceId, 'dieRoll', dieRoll);
return -1;
}
if(!dest.isSafePosition) await isSafeZoneAvoided(game,playerId,pieceId,dest)
game.publishHHGameEvent(playerId,"pawn_start",{
turn_id : game.getCurrentTurnId(),
update_time : Date.now(),
pawn_id : pieceId,
current_index : parseInt(curPosKey),
final_index : dest.posKey
})
game.incrPluginStats('ludo.processPiece.count');
game.timePluginStats('ludo.processPiece.timeTakenToPlay', timeTakenToPlay);
turnEventObj.end_value = dest.posKey
//move the piece to destination
movePiece(game, pieceId, dest);
//await handleSizes(game, curPosKey, dest.posKey);
// if destinations is heaven
if (dest.isHeaven) {
game.addTransient(GAME_PROPS.IS_EXTRA_ANIM_TIME_REQ, true);
piece._set(OBJECT_PROPS.SCORE,piece._get(OBJECT_PROPS.SCORE) + 56)
game.syncGameObjServVars(...[pieceId]);
scoreObj.message.extra = 56
scoreObj.score += 56
scoreObj.message = JSON.stringify(scoreObj.message)
game.awardScore(playerId,scoreObj)
logger.info(gk,"player %s scored %s",playerId,scoreObj)
game.sendTip(playerId, 14);
let curAscendCount = _getPlayerProp(game, playerId,
PLAYER_PROPS.ASCEND_COUNT) || 0;
_setPlayerProp(game, playerId, PLAYER_PROPS.ASCEND_COUNT,
curAscendCount + 1);
let homeEvent = new externalEventObjs.homeEvent();
homeEvent.current_home_count = curAscendCount + 1;
game.publishExternalGameEvent('BE_GAME_HOME_EVENT',playerId, homeEvent);
game.publishGameEvent(1603, [playerId]);
let piecesToAscend = meta.piecesToAscend || 1;
await handleSizes(game, curPosKey, dest.posKey);
if (curAscendCount + 1 >= piecesToAscend) {
_setPlayerProp(game, playerId, PLAYER_PROPS.IS_ASCENDED, true);
let gameOverTs = Date.now() + (meta.animTime || 1000);
_setGameProp(game, GAME_PROPS.GAME_OVER_TS, gameOverTs);
_setGameProp(game, GAME_PROPS.GAME_OVER_REASON,
enums.PIECE_HEAVEN_END_GAME_REASON);
logger.info(gk, 'Game marked to end at', gameOverTs, 'for reason',
enums.PIECE_HEAVEN_END_GAME_REASON);
return -1;
}
return 1;
}
if (dest.isSafePosition) {
scoreObj.message = JSON.stringify(scoreObj.message)
turnEventObj.order = true
logger.info(gk,"player %s scored %s",playerId,scoreObj)
game.awardScore(playerId,scoreObj)
logger.debug(gk, 'reached safe position by', playerId, 'position', dest);
game.publishGameEvent(1610,[playerId])
await handleSizes(game, curPosKey, dest.posKey);
return isRolledSix;
}
// fetch all pieces in that block
let allPieceIdsInBlock = await game.collidedObjsWithBoundaryProm(dest);
if (allPieceIdsInBlock.length > 2) { //> 2 safe point
scoreObj.message = JSON.stringify(scoreObj.message)
logger.info(gk,"player %s scored %s",playerId,scoreObj)
game.awardScore(playerId,scoreObj)
logger.info(gk, 'Multiple pieces on same spot, it is a safe spot',
pieceId,
allPieceIdsInBlock);
await handleSizes(game, curPosKey, dest.posKey);
return isRolledSix;
} else if (allPieceIdsInBlock.length === 2) {// 2 pieces then either kill or no kill
let isKilledAndPosInfo = await handleTwoPieces(game, playerId,
allPieceIdsInBlock, pieceId,scoreObj,turnEventObj);
let posKey = dest.posKey;
if(isKilledAndPosInfo.dest) {
posKey = isKilledAndPosInfo.dest.posKey;
await handleSizes(game, dest.posKey,posKey);
}
await handleSizes(game, curPosKey,dest.posKey);
return isKilledAndPosInfo.isContinue ?
isKilledAndPosInfo.isContinue : isRolledSix;
}
scoreObj.message = JSON.stringify(scoreObj.message)
logger.info(gk,"player %s scored %s",playerId,scoreObj)
game.awardScore(playerId,scoreObj)
await handleSizes(game, curPosKey, dest.posKey);
return isRolledSix // otherwise no turn continue
}
function movePiece(game, objId, dest,message,noUpdate) {
let objTrans = {
id: objId,
posKey: dest.posKey,
posX: dest.x,
posY: dest.y,
posZ: dest.z,
message :message
};
if(noUpdate) return;
game.forceUpdateGameObjects(objTrans);
}
async function handleSizes(game, prevPosKey, destPosKey) {
let meta = _getMeta(game);
const gk = {key: game.getId()};
let prevList = await game.collidedObjsWithBoundaryProm({posKey: prevPosKey});
let destList = await game.collidedObjsWithBoundaryProm({posKey: destPosKey});
if (logger.debug())
logger.debug(gk, 'Ids for list sizes are', stringify({prevList, destList}));
let prev = {r: 0, b: 0, y : 0, g :0};
let des = {r: 0, b: 0, y : 0, g :0};
for (let a of prevList) {
let ob = _gameObjById(game, a);
if (ob.type === "greenPiece")
prev.g++;
else if (ob.type === "redPiece")
prev.r++;
else if (ob.type === "yellowPiece")
prev.y++;
else if (ob.type === "bluePiece")
prev.b++;
}
for (let a of destList) {
let ob = _gameObjById(game, a);
if (ob.type === "greenPiece")
des.g++;
else if (ob.type === "redPiece")
des.r++;
else if (ob.type === "yellowPiece")
des.y++;
else if (ob.type === "bluePiece")
des.b++;
}
for (let a of prevList) {
let ob = _gameObjById(game, a);
if (ob.type === "greenPiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, prev.g);
ob._set(OBJECT_PROPS.OPP_SHARE, prev.b + prev.r + prev.y);
}else if (ob.type === "redPiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, prev.r);
ob._set(OBJECT_PROPS.OPP_SHARE, prev.b + prev.g + prev.y);
}else if (ob.type === "yellowPiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, prev.y);
ob._set(OBJECT_PROPS.OPP_SHARE, prev.b + prev.g + prev.r);
}else if (ob.type === "bluePiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, prev.b);
ob._set(OBJECT_PROPS.OPP_SHARE, prev.r + prev.g + prev.y);
}
}
for (let a of destList) {
let ob = _gameObjById(game, a);
if (ob.type === "greenPiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, des.g);
ob._set(OBJECT_PROPS.OPP_SHARE, des.b + des.r + des.y);
}else if (ob.type === "redPiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, des.r);
ob._set(OBJECT_PROPS.OPP_SHARE, des.b + des.g + des.y);
}else if (ob.type === "yellowPiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, des.y);
ob._set(OBJECT_PROPS.OPP_SHARE, des.b + des.g + des.r);
}else if (ob.type === "bluePiece") {
ob._set(OBJECT_PROPS.SELF_SHARE, des.b);
ob._set(OBJECT_PROPS.OPP_SHARE, des.r + des.g + des.y);
}
}
game.syncGameObjServVars(...prevList, ...destList);
}
async function handleTwoPieces(game, playerId, allPieceIdsInBlock, pieceId,scoreObj,
turnEventObj) {
const gk = {key: game.getId()};
const meta = _getMeta(game);
let currentBoard = getBord(game);
let prevPieceId = allPieceIdsInBlock[0] === pieceId ?
allPieceIdsInBlock[1] : allPieceIdsInBlock[0];
turnEventObj.genus = prevPieceId;
let prevPiece = _gameObjById(game, prevPieceId);
let curPiece = _gameObjById(game, pieceId);
let isAKill = prevPiece.type !== curPiece.type;
if (isAKill) {
game.addTransient(GAME_PROPS.IS_EXTRA_ANIM_TIME_REQ, true);
logger.info(gk, 'A kill by player', playerId, 'pieceId', pieceId, 'killed',
prevPieceId);
if(meta.noScoreOnKill) {
scoreObj.message = JSON.stringify(scoreObj.message)
game.awardScore(playerId, scoreObj)
}else {
scoreObj.message.extra = 7;
scoreObj.score += 7
scoreObj.message = JSON.stringify(scoreObj.message)
logger.info(gk, "player %s scored %s", playerId, scoreObj)
game.awardScore(playerId, scoreObj)
}
// let dest = await fetchEmptyHomeDest(game, prevPiece);
let dest = currentBoard.posInfoPieceInit(prevPiece.type)
let opponentId = game.getOwner(prevPieceId);
logger.info(gk,"score deducted from user %s with value &s",opponentId,
-prevPiece._get(OBJECT_PROPS.SCORE))
game.awardScore(opponentId,{
score : -prevPiece._get(OBJECT_PROPS.SCORE),
message : JSON.stringify({
extra : -prevPiece._get(OBJECT_PROPS.SCORE)
})}
)
let killedPawnPoints = prevPiece._get(OBJECT_PROPS.SCORE);
prevPiece._set(OBJECT_PROPS.SCORE,0)
game.syncGameObjServVars(...[prevPieceId]);
movePiece(game, prevPieceId, dest,'isKilled');
let curKillCount = _getPlayerProp(game, playerId, PLAYER_PROPS.KILLS);
let currOppKilledCount = _getPlayerProp(game , opponentId,PLAYER_PROPS.GOT_KILLED)
isPlayerFraudAndPublishEvent(game, playerId, opponentId);
_setPlayerProp(game, playerId, PLAYER_PROPS.KILLS, curKillCount + 1);
_setPlayerProp(game, opponentId,PLAYER_PROPS.GOT_KILLED,currOppKilledCount + 1)
let killTypes = _getPlayerProp(game, playerId, PLAYER_PROPS.KILL_TYPES);
killTypes.types.push(prevPiece.type);
_setPlayerProp(game, playerId, PLAYER_PROPS.KILL_TYPES, killTypes);
let killEvent = new externalEventObjs.killEvent();
killEvent.killer_score = game.getPlayerScore(playerId);
killEvent.victim_score = game.getPlayerScore(opponentId);
killEvent.killed_pawn_points = killedPawnPoints;
killEvent.killer_pawn_points = curPiece._get(OBJECT_PROPS.SCORE);
game.publishExternalGameEvent('BE_GAME_KILL',playerId, killEvent);
game.publishGameEvent(1601, [opponentId]);
game.publishGameEvent(1602, [playerId]);
game.sendTip(playerId, 13);
if (curKillCount === 0) game.publishGameEvent(1611, [playerId],
_getPlayerProp(game, playerId, PLAYER_PROPS.TURN_COUNT) + 1)
// send tips
/*
// holding tips for sometime
if (curKillCount === 0)
game.sendTip(playerId, 3);
else if (curKillCount === 1)
game.sendTip(playerId, 4);
*/
/*
// removed logic for max kills
if (curKillCount + 1 >= meta.maxPieceKills) {
let gameOverTs = Date.now() + (meta.animTime || 1000);
_setGameProp(game, GAME_PROPS.GAME_OVER_TS, gameOverTs);
logger.info(gk, 'Game marked to end at', gameOverTs);
_setGameProp(game, GAME_PROPS.GAME_OVER_REASON,
enums.PIECE_HEAVEN_END_GAME_REASON);
logger.info(gk, 'Game marked to end at', gameOverTs, 'for reason',
enums.MAX_KILLS_REACHED_REASON);
return -1;
}
*/
return {dest : dest, isContinue : 1};
} else {
scoreObj.message = JSON.stringify(scoreObj.message)
logger.info(gk,"player %s scored %s",playerId,scoreObj)
turnEventObj.order = true
game.awardScore(playerId,scoreObj)
game.sendTip(playerId, 10);
game.publishGameEvent(1610,[playerId])
return {isContinue : 0};
}
}
const isTurnSkip = function(game, playerId) {
if(_getGameProp(game,GAME_PROPS.IS_DICE_REVEALED)){
if(_getPlayerProp(game,playerId,PLAYER_PROPS.TURN_SKIP_DIE_ROLL_SIX)){
game.sendTip(playerId, 17);
return true;
}
return !isAtleastOnePawnSelectable(game,playerId);
}
return false;
};
async function fetchEmptyHomeDest(game, piece) {
let currentBoard = getBord(game)
let allHomeDest = currentBoard.pieceAllHomePos(piece.type);
for (let a of allHomeDest) {
let allPieceIdsInBlock = await game.collidedObjsWithBoundaryProm(a);
if (allPieceIdsInBlock.length === 0) return a;
}
}
function fetchInitSpawnPosition(game,piece){
}
function promisifyCollidedObjsWithBoundary(game) {
if (typeof game.collidedObjsWithBoundaryProm !== 'function') {
game.collidedObjsWithBoundaryProm = (boundary) => new Promise(
(resolve, reject) => {
game.collidedObjsWithBoundary(boundary, (err, res) => {
if (err) return reject(err);
return resolve(res);
});
});
}
}
function promisifyFetchBoundaryForObjId(game) {
if (typeof game.fetchBoundaryForObjIdProm !== 'function') {
game.fetchBoundaryForObjIdProm = (pieceId) => new Promise(
(resolve, reject) => {
game.fetchBoundaryForObjId(pieceId, (err, res) => {
if (err) return reject(err);
return resolve(res);
});
});
}
}
const rollADie = function(game, playerId) {
let meta = _getMeta(game);
let transKey = '_shuffler_' + playerId;
let roller = game.getTransient(transKey);
if (!roller) {
roller = new ShuffleRoller(meta, game.dieRollShuffledBag);
game.addTransient(transKey, roller);
}
let nextRoll = roller.nextRoll();
if(nextRoll != 6) _setPlayerProp(game, playerId, PLAYER_PROPS.CONSECUTIVE_SIXES,0);
logger.debug({key: game.getId()}, 'Next roll for', playerId, 'is', nextRoll);
return nextRoll;
};
async function destroyForfeitPlayerPawns(game){
const gk = {key: game.getId()};
let objListToDel = []
let currentBoard = getBord(game)
promisifyFetchBoundaryForObjId(game);
promisifyCollidedObjsWithBoundary(game);
for (let p of game.getAllPlayerIds()) {
logger.debug(gk,"forfeit for player ",p,game.getPlayerState(p),
_getPlayerProp(game,p,enums.PLAYER_PROPS.ARE_PAWNS_DESTROYED))
if(parseInt(game.getPlayerState(p)) === 30
&& !_getPlayerProp(game,p,enums.PLAYER_PROPS.ARE_PAWNS_DESTROYED)
&& game.getNumberOfPlayers() > 2){
logger.debug(gk,"forfeit for player ",p)
let objIds = game.getAllObjIds();
let pieceType = _getPlayerProp(game, p, enums.PLAYER_PROPS.COLOR);
for (let objId of objIds) {
let obj = _gameObjById(game, objId);
if (pieceType === obj.type) {
let curPosKey = await game.fetchBoundaryForObjIdProm(objId);
let curPosInfo = currentBoard.posInfoForPosKey(curPosKey);
logger.debug(gk,"moving ",obj ," with index ",curPosKey,
" curPosInfo ",curPosInfo ," to home")
obj._set(OBJECT_PROPS.SCORE,0)
if(!curPosInfo.isHome){
let dest = await fetchEmptyHomeDest(game, obj);
logger.debug(gk,"empty home pos",dest)
movePiece(game,objId,dest)
await handleSizes(game,curPosKey,dest.posKey)
}
objListToDel.push(objId)
}
}
logger.debug(gk,"forfeit game objects ",p,objListToDel)
game.destroyGameObjects(true,...objListToDel)
_setPlayerProp(game,p,enums.PLAYER_PROPS.ARE_PAWNS_DESTROYED,true)
}
}
}
function sendTurnInitHHEvent(game,playerId,diceRoll){
game.publishHHGameEvent(playerId,"turn_init",{
turn_id: game.getCurrentTurnId()+1,
roll: diceRoll,
update_time: Date.now(),
scores: getSeatingPlayerScore(game)
}
)
}
function getSeatingPlayerScore (game){
let scores = []
for (let p of game.getPlayerSeating()) {
scores.push(game.getPlayerScore(p))
}
return scores
}
function disablePawns(game,playerId ){
let pieceType = _getPlayerProp(game,playerId,PLAYER_PROPS.COLOR)
let objIds = game.getAllObjIds();
let objIdList = [];
for (let objId of objIds) {
let obj = _gameObjById(game, objId)
if(pieceType === obj.type) {
objIdList.push(objId)
obj._set(OBJECT_PROPS.CAN_SELECT, false)
}
}
if(objIdList.length > 0)
game.syncGameObjServVars(...objIdList);
}
async function enablePawns(game,playerId){
let currentBoard = getBord(game)
const gk = {key: game.getId()};
promisifyFetchBoundaryForObjId(game);
promisifyCollidedObjsWithBoundary(game);
let dieRoll = parseInt(
_getPlayerProp(game, playerId, PLAYER_PROPS.DIE_ROLL));
let pieceType = _getPlayerProp(game,playerId,PLAYER_PROPS.COLOR)
let objIds = game.getAllObjIds();
let objIdList = [];
for (let objId of objIds) {
let obj = _gameObjById(game, objId)
let curPosKey = await game.fetchBoundaryForObjIdProm(objId);
if(pieceType === obj.type) {
let dist = currentBoard.calculateDist(obj.type, curPosKey, dieRoll,logger);
if(isNaN(dist)) {
if(!obj._get(OBJECT_PROPS.CAN_SELECT)) {
objIdList.push(objId)
obj._set(OBJECT_PROPS.CAN_SELECT, true)
}
}
if (dist > 0) {
objIdList.push(objId)
obj._set(OBJECT_PROPS.CAN_SELECT, false)
} else {
if(!obj._get(OBJECT_PROPS.CAN_SELECT)) {
objIdList.push(objId)
obj._set(OBJECT_PROPS.CAN_SELECT, true)
}
}
}
}
if(objIdList.length > 0)
game.syncGameObjServVars(...objIdList);
if(!isAtleastOnePawnSelectable(game,playerId)) {
_setGameProp(game, GAME_PROPS.NO_POSSIBLE_MOVE, false);
}
}
async function isSafeZoneAvoided(game,playerId,pawnId,dest){
const meta = _getMeta(game);
const maxTurnCountRuleTwo = parseInt(meta.maxTurnCountRuleTwo);
const curTurnCount = _getPlayerProp(game, playerId, PLAYER_PROPS.TURN_COUNT);
if(curTurnCount > maxTurnCountRuleTwo) return;
let currentBoard = getBord(game)
const gk = {key: game.getId()};
logger.debug(gk,"turn count %s and threashold %s " , curTurnCount,maxTurnCountRuleTwo)
logger.debug(gk,"validating safe zone is pawn",playerId)
promisifyFetchBoundaryForObjId(game);
promisifyCollidedObjsWithBoundary(game);
let missedSafeZones = 0
let dieRoll = _getPlayerProp(game, playerId, PLAYER_PROPS.DIE_ROLL)
let pieceType = _getPlayerProp(game,playerId,PLAYER_PROPS.COLOR)
let objIds = game.getAllObjIds();
for (let objId of objIds) {
let obj = _gameObjById(game, objId)
if(pieceType === obj.type) {
logger.debug(gk," safe zone check for",playerId,dest,objId)
if(objId !== pawnId){
logger.debug(gk," safe zone validation for missed",playerId,dest)
let curPosKey = await game.fetchBoundaryForObjIdProm(objId);
let pDest = currentBoard.nextDestination(obj.type, curPosKey, dieRoll, game, playerId);
if (pDest && pDest.isSafePosition) {
logger.debug(gk, "safe zone reach missed", playerId, pDest)
missedSafeZones++
}
}
}
}
if(missedSafeZones > 0 ){
let pMissedSafeZonesCount = _getPlayerProp(game,playerId,PLAYER_PROPS.MISSED_SAFE_ZONES)
_setPlayerProp(game,playerId,PLAYER_PROPS.MISSED_SAFE_ZONES,pMissedSafeZonesCount + missedSafeZones)
}
}
const getAbsoluteGameResult = function(game, scoreList) {
const gk = {key: game.getId()};
scoreList.sort((a, b) => b.score - a.score);
let res = []
let startPos = 0;
for(let i = 0 ; i < scoreList.length ; i++){
let s = scoreList[i]
let totalMatches = 0;
for(let j = i + 1 ; j < scoreList.length ; j++){
if(s.score !== scoreList[j].score){
_setGameProp(game,GAME_PROPS.ABSOLUTE_WIN_TYPE,2)
break;
}else {
logger.info(gk,"score list tie found",scoreList[i],"and",scoreList[j])
totalMatches++
}
}
logger.info(gk,"total matches found",totalMatches)
// todo add proper wintypes
if(totalMatches === 0 ) {
let winType = 1
if(startPos !== 0) winType = 2
startPos = startPos + 1;
res.push({userId: s.userId, winType: winType, position: startPos})
}else {
let tiePlayers = []
let playerScoreMap = new Map();
for(let z = 0 ; z <= totalMatches ; z++){
playerScoreMap.set(scoreList[i+z].playerId,scoreList[i+z])
tiePlayers.push(scoreList[i+z].playerId)
}
logger.info(gk,"total tie players found",tiePlayers)
let winnersList = _findAbsoluteWinner(game,tiePlayers);
logger.info(gk," tie winners ",winnersList)
for(let winnerPlayer of winnersList){
startPos = startPos + 1;
let playerWinInfo = playerScoreMap.get(winnerPlayer)
let winType = 1
if(startPos !== 0) winType = 2
res.push({userId: playerWinInfo.userId, winType: winType, position: startPos})
}
i += totalMatches;
}
}
game.syncGameStateServVars();
return res;
}
function isPlayerFraudAndPublishEvent(game, playerId, opponentPlayerId) {
// Player is killing Opponent
const curTurnCount = _getPlayerProp(game, playerId, PLAYER_PROPS.TURN_COUNT);
const gk = {key: game.getId()};// player current Turn Count
const opponentUserId = game.getUserIdByPlayerId(opponentPlayerId);
const key = `${opponentPlayerId.toString()}_killCount`;
let value = parseInt(game.getPlayerProp(playerId, key));
game.setPlayerProp(playerId, key, value + 1);
value += 1;
const meta = _getMeta(game);
const minKillCountRuleOne = parseInt(meta.minKillCountRuleOne);
const minKillCountRuleTwo = parseInt(meta.minKillCountRuleTwo);
const maxTurnCountRuleOne = parseInt(meta.maxTurnCountRuleOne);
const maxTurnCountRuleTwo = parseInt(meta.maxTurnCountRuleTwo);
let fraudUserEvent;
if(value === minKillCountRuleOne && curTurnCount <= maxTurnCountRuleOne) {
fraudUserEvent = new eventObjs.fraudFirstKill();
fraudUserEvent.userId2 = opponentUserId;
game.publishGameFraudEvent(playerId,fraudUserEvent);
}
else if(value === minKillCountRuleTwo && curTurnCount <= maxTurnCountRuleTwo) {
fraudUserEvent = new eventObjs.fraudQuadrantKill();
fraudUserEvent.userId2 = opponentUserId;
let pMissedSafeZonesCount = _getPlayerProp(game,opponentPlayerId,PLAYER_PROPS.MISSED_SAFE_ZONES)
let qtkSafeZoneMissedThreashold = parseInt(meta.qtkSafeZoneMissedThreashold) || 2
if(pMissedSafeZonesCount > qtkSafeZoneMissedThreashold){
let fraudUserSafeZoneEvent = new eventObjs.fraudQuadrantKillSafeZone();
fraudUserSafeZoneEvent.userId2 = opponentUserId;
game.publishGameFraudEvent(playerId,fraudUserSafeZoneEvent);
}
logger.debug(gk,"missed safe zone count %s " , pMissedSafeZonesCount)
game.publishGameFraudEvent(playerId,fraudUserEvent);
}
}
const initPlayerShuffleRoller = function(game , playerId){
let meta = _getMeta(game);
let transKey = '_shuffler_' + playerId;
let roller = game.getTransient(transKey);
if (!roller) {
roller = new ShuffleRoller(meta, game.dieRollShuffledBag);
game.addTransient(transKey, roller);
}
}
module.exports = {
gameAction,
turnInit,
turnEndHandler,
rollADie,
issueTurnStartSavePoint,
isTurnSkip,
destroyForfeitPlayerPawns,
enablePawns,
getAbsoluteGameResult,
sendTurnInitHHEvent,
initPlayerShuffleRoller
};