171 lines
6.6 KiB
JavaScript
171 lines
6.6 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.matchSignal = exports.matchTerminate = exports.matchLoop = exports.matchLeave = exports.matchJoin = exports.matchJoinAttempt = exports.matchInit = void 0;
|
|
var types_1 = require("./types");
|
|
var MAX_PLAYERS = 4;
|
|
var GRID_SIZE = 100; // 10x10
|
|
var BOMB_COUNT = 30;
|
|
var ITEM_COUNT_MIN = 5;
|
|
var ITEM_COUNT_MAX = 10;
|
|
var matchInit = function (ctx, logger, nk, params) {
|
|
logger.info('Match initialized');
|
|
// Generate Grid
|
|
var grid = Array(GRID_SIZE).fill(null).map(function () { return ({ type: 'empty', revealed: false }); });
|
|
// Place Bombs
|
|
var bombsPlaced = 0;
|
|
while (bombsPlaced < BOMB_COUNT) {
|
|
var idx = Math.floor(Math.random() * GRID_SIZE);
|
|
if (grid[idx].type === 'empty') {
|
|
grid[idx].type = 'bomb';
|
|
bombsPlaced++;
|
|
}
|
|
}
|
|
// Place Items
|
|
var itemCount = Math.floor(Math.random() * (ITEM_COUNT_MAX - ITEM_COUNT_MIN + 1)) + ITEM_COUNT_MIN;
|
|
var itemsPlaced = 0;
|
|
while (itemsPlaced < itemCount) {
|
|
var idx = Math.floor(Math.random() * GRID_SIZE);
|
|
if (grid[idx].type === 'empty') {
|
|
grid[idx].type = 'item';
|
|
grid[idx].itemId = types_1.ItemTypes[Math.floor(Math.random() * types_1.ItemTypes.length)];
|
|
itemsPlaced++;
|
|
}
|
|
}
|
|
var state = {
|
|
players: {},
|
|
grid: grid,
|
|
turnOrder: [],
|
|
currentTurnIndex: 0,
|
|
round: 1,
|
|
winnerId: null,
|
|
gameStarted: false
|
|
};
|
|
return {
|
|
state: state,
|
|
tickRate: 1, // 1 tick per second is enough for turn-based, but maybe higher for responsiveness
|
|
label: 'Animal Minesweeper'
|
|
};
|
|
};
|
|
exports.matchInit = matchInit;
|
|
var matchJoinAttempt = function (ctx, logger, nk, dispatcher, tick, state, presence, metadata) {
|
|
if (state.gameStarted) {
|
|
return { state: state, accept: false, rejectMessage: 'Game already started' };
|
|
}
|
|
if (Object.keys(state.players).length >= MAX_PLAYERS) {
|
|
return { state: state, accept: false, rejectMessage: 'Match full' };
|
|
}
|
|
return { state: state, accept: true };
|
|
};
|
|
exports.matchJoinAttempt = matchJoinAttempt;
|
|
var matchJoin = function (ctx, logger, nk, dispatcher, tick, state, presences) {
|
|
presences.forEach(function (presence) {
|
|
// Random character assignment for now
|
|
var character = types_1.CharacterTypes[Math.floor(Math.random() * types_1.CharacterTypes.length)];
|
|
state.players[presence.userId] = {
|
|
userId: presence.userId,
|
|
sessionId: presence.sessionId,
|
|
username: presence.username,
|
|
avatar: '🦁', // Placeholder, map character to emoji later
|
|
hp: 4, // Default HP
|
|
maxHp: 4,
|
|
status: [],
|
|
character: character
|
|
};
|
|
state.turnOrder.push(presence.userId);
|
|
logger.info("Player joined: ".concat(presence.userId));
|
|
});
|
|
// Check if full to start game
|
|
// Fix: Use >= to handle potential race condition where multiple players join in same tick
|
|
if (Object.keys(state.players).length >= MAX_PLAYERS && !state.gameStarted) {
|
|
state.gameStarted = true;
|
|
logger.info('Game Started! Broadcasting GAME_START');
|
|
// Broadcast Game Start
|
|
dispatcher.broadcastMessage(types_1.OpCode.GAME_START, JSON.stringify(state));
|
|
}
|
|
else {
|
|
logger.info("Player joined. Count: ".concat(Object.keys(state.players).length, ". Broadcasting UPDATE_STATE"));
|
|
// Broadcast updated state (new player joined)
|
|
dispatcher.broadcastMessage(types_1.OpCode.UPDATE_STATE, JSON.stringify(state));
|
|
}
|
|
return { state: state };
|
|
};
|
|
exports.matchJoin = matchJoin;
|
|
var matchLeave = function (ctx, logger, nk, dispatcher, tick, state, presences) {
|
|
presences.forEach(function (presence) {
|
|
delete state.players[presence.userId];
|
|
var idx = state.turnOrder.indexOf(presence.userId);
|
|
if (idx > -1) {
|
|
state.turnOrder.splice(idx, 1);
|
|
// Adjust turn index if necessary
|
|
if (idx < state.currentTurnIndex) {
|
|
state.currentTurnIndex--;
|
|
}
|
|
if (state.currentTurnIndex >= state.turnOrder.length) {
|
|
state.currentTurnIndex = 0;
|
|
}
|
|
}
|
|
});
|
|
// Broadcast updated state (player left)
|
|
if (state.gameStarted && Object.keys(state.players).length > 0) {
|
|
dispatcher.broadcastMessage(types_1.OpCode.UPDATE_STATE, JSON.stringify(state));
|
|
}
|
|
// If game in progress and players drop, handle logic (end game or continue)
|
|
if (Object.keys(state.players).length < 2 && state.gameStarted) {
|
|
state.winnerId = Object.keys(state.players)[0] || null;
|
|
dispatcher.broadcastMessage(types_1.OpCode.GAME_OVER, JSON.stringify({ winnerId: state.winnerId }));
|
|
return null; // End match
|
|
}
|
|
// Broadcast updated state (player left)
|
|
if (state.gameStarted) {
|
|
dispatcher.broadcastMessage(types_1.OpCode.UPDATE_STATE, JSON.stringify(state));
|
|
}
|
|
return { state: state };
|
|
};
|
|
exports.matchLeave = matchLeave;
|
|
var matchLoop = function (ctx, logger, nk, dispatcher, tick, state, messages) {
|
|
if (!state.gameStarted)
|
|
return { state: state };
|
|
messages.forEach(function (message) {
|
|
if (message.opCode === types_1.OpCode.MOVE) {
|
|
var data = JSON.parse(nk.binaryToString(message.data));
|
|
handleMove(state, message.sender.userId, data.index, logger, dispatcher);
|
|
}
|
|
});
|
|
return { state: state };
|
|
};
|
|
exports.matchLoop = matchLoop;
|
|
var matchTerminate = function (ctx, logger, nk, dispatcher, tick, state, graceSeconds) {
|
|
return { state: state };
|
|
};
|
|
exports.matchTerminate = matchTerminate;
|
|
var matchSignal = function (ctx, logger, nk, dispatcher, tick, state, data) {
|
|
return { state: state, data: data };
|
|
};
|
|
exports.matchSignal = matchSignal;
|
|
// --- Helper Functions ---
|
|
function handleMove(state, userId, cellIndex, logger, dispatcher) {
|
|
// Validate Turn
|
|
var currentUserId = state.turnOrder[state.currentTurnIndex];
|
|
if (userId !== currentUserId) {
|
|
return; // Not your turn
|
|
}
|
|
var cell = state.grid[cellIndex];
|
|
if (cell.revealed)
|
|
return; // Already revealed
|
|
// Reveal Cell
|
|
cell.revealed = true;
|
|
var player = state.players[userId];
|
|
// Logic
|
|
if (cell.type === 'bomb') {
|
|
player.hp -= 2;
|
|
// Check death...
|
|
}
|
|
else if (cell.type === 'item') {
|
|
// Add item logic...
|
|
}
|
|
// Next Turn
|
|
state.currentTurnIndex = (state.currentTurnIndex + 1) % state.turnOrder.length;
|
|
// Broadcast Update
|
|
dispatcher.broadcastMessage(types_1.OpCode.UPDATE_STATE, JSON.stringify(state));
|
|
}
|