Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
397 lines
12 KiB
JavaScript
397 lines
12 KiB
JavaScript
/* main.js – runs the demo with your prototype-based grid */
|
||
|
||
var moth = use('moth')
|
||
var json = use('json')
|
||
|
||
var res = 160
|
||
var internal_res = 480
|
||
|
||
moth.initialize({ width:res, height:res, resolution_x:internal_res, resolution_y:internal_res, mode:'letterbox' });
|
||
|
||
var os = use('os');
|
||
var draw2d = use('draw2d');
|
||
var gfx = use('graphics');
|
||
|
||
/*──── import our pieces + systems ───────────────────────────────────*/
|
||
var Grid = use('grid'); // your new ctor
|
||
var MovementSystem = use('movement').MovementSystem;
|
||
var startingPos = use('pieces').startingPosition;
|
||
var rules = use('rules');
|
||
|
||
/*──── build board ───────────────────────────────────────────────────*/
|
||
var grid = new Grid(8, 8);
|
||
grid.width = 8; // (the ctor didn't store them)
|
||
grid.height = 8;
|
||
|
||
var mover = new MovementSystem(grid, rules);
|
||
startingPos(grid);
|
||
|
||
/*──── networking and game state ─────────────────────────────────────*/
|
||
var gameState = 'waiting'; // 'waiting', 'searching', 'server_waiting', 'connected'
|
||
var isServer = false;
|
||
var opponent = null;
|
||
var myColor = null; // 'white' or 'black'
|
||
var isMyTurn = false;
|
||
|
||
function updateTitle() {
|
||
var title = "Misty Chess - ";
|
||
|
||
switch(gameState) {
|
||
case 'waiting':
|
||
title += "Press S to start server or J to join";
|
||
break;
|
||
case 'searching':
|
||
title += "Searching for server...";
|
||
break;
|
||
case 'server_waiting':
|
||
title += "Waiting for player to join...";
|
||
break;
|
||
case 'connected':
|
||
if (myColor) {
|
||
title += (mover.turn === myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
|
||
} else {
|
||
title += mover.turn + " turn";
|
||
}
|
||
break;
|
||
}
|
||
|
||
prosperon.window.title = title
|
||
}
|
||
|
||
// Initialize title
|
||
updateTitle();
|
||
|
||
/*──── mouse → click-to-move ─────────────────────────────────────────*/
|
||
var selectPos = null;
|
||
var hoverPos = null;
|
||
var holdingPiece = false;
|
||
|
||
var opponentMousePos = null;
|
||
var opponentHoldingPiece = false;
|
||
var opponentSelectPos = null;
|
||
|
||
prosperon.on('mouse_button_down', function(e) {
|
||
if (e.which !== 0) return;
|
||
|
||
// Don't allow piece selection unless we have an opponent
|
||
if (gameState !== 'connected' || !opponent) return;
|
||
|
||
var mx = e.mouse.x;
|
||
var my = e.mouse.y;
|
||
|
||
var c = [Math.floor(mx / 60), Math.floor(my / 60)];
|
||
if (!grid.inBounds(c)) return;
|
||
|
||
var cell = grid.at(c);
|
||
if (cell.length && cell[0].colour === mover.turn) {
|
||
selectPos = c;
|
||
holdingPiece = true;
|
||
// Send pickup notification to opponent
|
||
if (opponent) {
|
||
$_.send(opponent, {
|
||
type: 'piece_pickup',
|
||
pos: c
|
||
});
|
||
}
|
||
} else {
|
||
selectPos = null;
|
||
}
|
||
})
|
||
|
||
prosperon.on('mouse_button_up', function(e) {
|
||
if (e.which !== 0 || !holdingPiece || !selectPos) return;
|
||
|
||
// Don't allow moves unless we have an opponent and it's our turn
|
||
if (gameState !== 'connected' || !opponent || !isMyTurn) {
|
||
holdingPiece = false;
|
||
return;
|
||
}
|
||
|
||
var mx = e.mouse.x;
|
||
var my = e.mouse.y;
|
||
|
||
var c = [Math.floor(mx / 60), Math.floor(my / 60)];
|
||
if (!grid.inBounds(c)) {
|
||
holdingPiece = false;
|
||
return;
|
||
}
|
||
|
||
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
||
console.log("Made move from", selectPos, "to", c);
|
||
// Send move to opponent
|
||
console.log("Sending move to opponent:", opponent);
|
||
$_.send(opponent, {
|
||
type: 'move',
|
||
from: selectPos,
|
||
to: c
|
||
});
|
||
isMyTurn = false; // It's now opponent's turn
|
||
console.log("Move sent, now opponent's turn");
|
||
selectPos = null;
|
||
updateTitle();
|
||
}
|
||
|
||
holdingPiece = false;
|
||
|
||
// Send piece drop notification to opponent
|
||
if (opponent) {
|
||
$_.send(opponent, {
|
||
type: 'piece_drop'
|
||
});
|
||
}
|
||
})
|
||
|
||
prosperon.on('mouse_motion', function(e) {
|
||
var mx = e.pos.x;
|
||
var my = e.pos.y;
|
||
|
||
var c = [Math.floor(mx / 60), Math.floor(my / 60)];
|
||
if (!grid.inBounds(c)) {
|
||
hoverPos = null;
|
||
return;
|
||
}
|
||
|
||
hoverPos = c;
|
||
|
||
// Send mouse position to opponent in real-time
|
||
if (opponent && gameState === 'connected') {
|
||
$_.send(opponent, {
|
||
type: 'mouse_move',
|
||
pos: c,
|
||
holding: holdingPiece,
|
||
selectPos: selectPos
|
||
});
|
||
}
|
||
})
|
||
|
||
/*──── drawing helpers ───────────────────────────────────────────────*/
|
||
/* ── constants ─────────────────────────────────────────────────── */
|
||
var S = 60; // square size in px
|
||
var light = [0.93,0.93,0.93,1];
|
||
var dark = [0.25,0.25,0.25,1];
|
||
var allowedColor = [1.0, 0.84, 0.0, 1.0]; // Gold for allowed moves
|
||
var myMouseColor = [0.0, 1.0, 0.0, 1.0]; // Green for my mouse
|
||
var opponentMouseColor = [1.0, 0.0, 0.0, 1.0]; // Red for opponent mouse
|
||
|
||
/* ── draw one 8×8 chess board ──────────────────────────────────── */
|
||
function drawBoard() {
|
||
for (var y = 0; y < 8; ++y)
|
||
for (var x = 0; x < 8; ++x) {
|
||
var isMyHover = hoverPos && hoverPos[0] === x && hoverPos[1] === y;
|
||
var isOpponentHover = opponentMousePos && opponentMousePos[0] === x && opponentMousePos[1] === y;
|
||
var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]);
|
||
|
||
var color = ((x+y)&1) ? dark : light;
|
||
|
||
if (isValidMove) {
|
||
color = allowedColor; // Gold for allowed moves
|
||
} else if (isMyHover && !isOpponentHover) {
|
||
color = myMouseColor; // Green for my mouse
|
||
} else if (isOpponentHover) {
|
||
color = opponentMouseColor; // Red for opponent mouse
|
||
}
|
||
|
||
draw2d.rectangle(
|
||
{ x: x*S, y: y*S, width: S, height: S },
|
||
{ thickness: 0, color: color }
|
||
);
|
||
}
|
||
}
|
||
|
||
function isValidMoveForTurn(from, to) {
|
||
if (!grid.inBounds(to)) return false;
|
||
|
||
var piece = grid.at(from)[0];
|
||
if (!piece) return false;
|
||
|
||
// Check if the destination has a piece of the same color
|
||
var destCell = grid.at(to);
|
||
if (destCell.length && destCell[0].colour === piece.colour) {
|
||
return false;
|
||
}
|
||
|
||
return rules.canMove(piece, from, to, grid);
|
||
}
|
||
|
||
/* ── draw every live piece ─────────────────────────────────────── */
|
||
function drawPieces() {
|
||
grid.each(function (piece) {
|
||
if (piece.captured) return;
|
||
|
||
// Skip drawing the piece being held (by me or opponent)
|
||
if (holdingPiece && selectPos &&
|
||
piece.coord[0] === selectPos[0] &&
|
||
piece.coord[1] === selectPos[1]) {
|
||
return;
|
||
}
|
||
|
||
// Skip drawing the piece being held by opponent
|
||
if (opponentHoldingPiece && opponentSelectPos &&
|
||
piece.coord[0] === opponentSelectPos[0] &&
|
||
piece.coord[1] === opponentSelectPos[1]) {
|
||
return;
|
||
}
|
||
|
||
var r = { x: piece.coord[0]*S, y: piece.coord[1]*S,
|
||
width:S, height:S };
|
||
|
||
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
||
});
|
||
|
||
// Draw the held piece at the mouse position if we're holding one
|
||
if (holdingPiece && selectPos && hoverPos) {
|
||
var piece = grid.at(selectPos)[0];
|
||
if (piece) {
|
||
var r = { x: hoverPos[0]*S, y: hoverPos[1]*S,
|
||
width:S, height:S };
|
||
|
||
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
||
}
|
||
}
|
||
|
||
// Draw opponent's held piece if they're dragging one
|
||
if (opponentHoldingPiece && opponentSelectPos && opponentMousePos) {
|
||
var opponentPiece = grid.at(opponentSelectPos)[0];
|
||
if (opponentPiece) {
|
||
var r = { x: opponentMousePos[0]*S, y: opponentMousePos[1]*S,
|
||
width:S, height:S };
|
||
|
||
// Draw with slight transparency to show it's the opponent's piece
|
||
draw2d.image(opponentPiece.sprite, r, 0, [0,0], [0,0], {mode:"nearest", color: [1, 1, 1, 0.7]});
|
||
}
|
||
}
|
||
}
|
||
|
||
prosperon.on('draw', function() {
|
||
drawBoard()
|
||
drawPieces()
|
||
})
|
||
|
||
prosperon.on('key_down', function(e) {
|
||
// S key - start server
|
||
if (e.scancode === 22 && gameState === 'waiting') { // S key
|
||
startServer();
|
||
}
|
||
// J key - join server
|
||
else if (e.scancode === 13 && gameState === 'waiting') { // J key
|
||
joinServer();
|
||
}
|
||
})
|
||
|
||
function startServer() {
|
||
gameState = 'server_waiting';
|
||
isServer = true;
|
||
myColor = 'white';
|
||
isMyTurn = true;
|
||
updateTitle();
|
||
|
||
$_.portal(e => {
|
||
console.log("Portal received contact message");
|
||
// Reply with this actor to establish connection
|
||
$_.send(e, $_);
|
||
console.log("Portal replied with server actor");
|
||
}, 5678);
|
||
}
|
||
|
||
function joinServer() {
|
||
gameState = 'searching';
|
||
updateTitle();
|
||
|
||
function contact_fn(actor, reason) {
|
||
console.log("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
||
if (actor) {
|
||
opponent = actor;
|
||
console.log("Connection established with server, sending join request");
|
||
|
||
// Send a greet message with our actor object
|
||
$_.send(opponent, {
|
||
type: 'greet',
|
||
client_actor: $_
|
||
});
|
||
} else {
|
||
console.log(`Failed to connect: ${json.encode(reason)}`);
|
||
gameState = 'waiting';
|
||
updateTitle();
|
||
}
|
||
}
|
||
|
||
$_.contact(contact_fn, {
|
||
address: "localhost",
|
||
port: 5678
|
||
});
|
||
}
|
||
|
||
var os = use('os')
|
||
|
||
// Set up IO actor subscription
|
||
var ioguy = {
|
||
__ACTORDATA__: {
|
||
id: os.ioactor()
|
||
}
|
||
};
|
||
|
||
$_.send(ioguy, {
|
||
type: "subscribe",
|
||
actor: $_
|
||
});
|
||
|
||
$_.receiver(e => {
|
||
if (e.type === 'game_start' || e.type === 'move' || e.type === 'greet')
|
||
console.log("Receiver got message:", e.type, e);
|
||
if (e.type === 'quit') os.exit()
|
||
|
||
if (e.type === 'greet') {
|
||
console.log("Server received greet from client");
|
||
// Store the client's actor object for ongoing communication
|
||
opponent = e.client_actor;
|
||
console.log("Stored client actor:", json.encode(opponent));
|
||
gameState = 'connected';
|
||
updateTitle();
|
||
|
||
// Send game_start to the client
|
||
console.log("Sending game_start to client");
|
||
$_.send(opponent, {
|
||
type: 'game_start',
|
||
your_color: 'black'
|
||
});
|
||
console.log("game_start message sent to client");
|
||
}
|
||
else if (e.type === 'game_start') {
|
||
console.log("Game starting, I am:", e.your_color);
|
||
myColor = e.your_color;
|
||
isMyTurn = (myColor === 'white');
|
||
gameState = 'connected';
|
||
updateTitle();
|
||
} else if (e.type === 'move') {
|
||
console.log("Received move from opponent:", e.from, "to", e.to);
|
||
// Apply opponent's move
|
||
var fromCell = grid.at(e.from);
|
||
if (fromCell.length) {
|
||
var piece = fromCell[0];
|
||
if (mover.tryMove(piece, e.to)) {
|
||
isMyTurn = true; // It's now our turn
|
||
updateTitle();
|
||
console.log("Applied opponent move, now my turn");
|
||
} else {
|
||
console.log("Failed to apply opponent move");
|
||
}
|
||
} else {
|
||
console.log("No piece found at from position");
|
||
}
|
||
} else if (e.type === 'mouse_move') {
|
||
// Update opponent's mouse position
|
||
opponentMousePos = e.pos;
|
||
opponentHoldingPiece = e.holding;
|
||
opponentSelectPos = e.selectPos;
|
||
} else if (e.type === 'piece_pickup') {
|
||
// Opponent picked up a piece
|
||
opponentSelectPos = e.pos;
|
||
opponentHoldingPiece = true;
|
||
} else if (e.type === 'piece_drop') {
|
||
// Opponent dropped their piece
|
||
opponentHoldingPiece = false;
|
||
opponentSelectPos = null;
|
||
}
|
||
|
||
prosperon.dispatch(e.type, e)
|
||
}) |