networked chess
This commit is contained in:
@@ -1,18 +1,17 @@
|
||||
/* 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
|
||||
var render = use('render');
|
||||
render.initialize({ width:res, height:res, resolution_x:internal_res, resolution_y:internal_res, mode:'letterbox' });
|
||||
|
||||
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');
|
||||
|
||||
/* camera – unchanged */
|
||||
var camera = { size:[internal_res,internal_res], transform:os.make_transform(), fov:50, near_z:0,
|
||||
far_z:1000, surface:undefined, viewport:{x:0,y:0,width:1,height:1},
|
||||
ortho:true, anchor:[0,0] };
|
||||
|
||||
/*──── import our pieces + systems ───────────────────────────────────*/
|
||||
var Grid = use('grid'); // your new ctor
|
||||
var MovementSystem = use('movement').MovementSystem;
|
||||
@@ -21,97 +20,281 @@ var rules = use('rules');
|
||||
|
||||
/*──── build board ───────────────────────────────────────────────────*/
|
||||
var grid = new Grid(8, 8);
|
||||
grid.width = 8; // (the ctor didn’t store them)
|
||||
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;
|
||||
prosperon.window.on_mousedown = function (btn, mx, my) {
|
||||
if (btn !== 0) return;
|
||||
var hoverPos = null;
|
||||
var holdingPiece = false;
|
||||
|
||||
prosperon.on('mouse_button_down', function(e) {
|
||||
if (e.which !== 0) 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 (selectPos && mover.tryMove(grid.at(selectPos)[0], c)) {
|
||||
selectPos = null; // made a move
|
||||
return;
|
||||
}
|
||||
if (cell.length && cell[0].colour === mover.turn) {
|
||||
selectPos = c; // pick up piece
|
||||
selectPos = c;
|
||||
holdingPiece = true;
|
||||
} else {
|
||||
selectPos = null;
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
prosperon.on('mouse_button_up', function(e) {
|
||||
if (e.which !== 0 || !holdingPiece || !selectPos) 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;
|
||||
}
|
||||
|
||||
// Only allow moves if it's our turn or we're in local mode
|
||||
if (gameState !== 'connected' || isMyTurn) {
|
||||
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
||||
// Send move to opponent if connected
|
||||
if (gameState === 'connected' && opponent) {
|
||||
$_.send(opponent, {
|
||||
type: 'move',
|
||||
from: selectPos,
|
||||
to: c
|
||||
});
|
||||
isMyTurn = false; // It's now opponent's turn
|
||||
}
|
||||
selectPos = null;
|
||||
updateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
holdingPiece = false;
|
||||
})
|
||||
|
||||
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;
|
||||
})
|
||||
|
||||
/*──── 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 hoverColor = [0.8, 1.0, 0.8, 1.0];
|
||||
var validMoveColor = [1.0, 0.8, 0.8, 1.0];
|
||||
|
||||
/* ── draw one 8×8 chess board ──────────────────────────────────── */
|
||||
function drawBoard() {
|
||||
for (var y = 0; y < 8; ++y)
|
||||
for (var x = 0; x < 8; ++x) {
|
||||
var isHovered = hoverPos && hoverPos[0] === x && hoverPos[1] === y;
|
||||
var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]);
|
||||
|
||||
var color = ((x+y)&1) ? dark : light;
|
||||
|
||||
if (isHovered && !isValidMove) {
|
||||
color = hoverColor;
|
||||
} else if (isValidMove) {
|
||||
color = validMoveColor;
|
||||
}
|
||||
|
||||
draw2d.rectangle(
|
||||
{ x: x*S, y: y*S, width: S, height: S },
|
||||
{ thickness: 0, color: ((x+y)&1) ? dark : light }
|
||||
{ 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
|
||||
if (holdingPiece && selectPos &&
|
||||
piece.coord[0] === selectPos[0] &&
|
||||
piece.coord[1] === selectPos[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"});
|
||||
});
|
||||
}
|
||||
|
||||
/*──── main loop ─────────────────────────────────────────────────────*/
|
||||
var last = os.now(), fpsTimer = 0, fpsCount = 0;
|
||||
|
||||
function loop() {
|
||||
var now = os.now(), dt = now - last; last = now;
|
||||
|
||||
render.clear([22/255, 120/255, 194/255, 1]);
|
||||
render.camera(camera);
|
||||
drawBoard();
|
||||
drawPieces();
|
||||
render.present();
|
||||
|
||||
fpsTimer += dt; fpsCount++;
|
||||
if (fpsTimer >= 0.5) {
|
||||
prosperon.window.title =
|
||||
'Chess demo – ' + mover.turn + '\'s move – FPS ' + (fpsCount / fpsTimer).toFixed(1);
|
||||
fpsTimer = fpsCount = 0;
|
||||
|
||||
// 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"});
|
||||
}
|
||||
}
|
||||
$_.delay(loop, Math.max(0, (1 / 60) - (os.now() - now)));
|
||||
}
|
||||
loop();
|
||||
|
||||
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 => {
|
||||
opponent = e;
|
||||
gameState = 'connected';
|
||||
updateTitle();
|
||||
|
||||
// Tell the joining player they are black
|
||||
$_.send(opponent, {
|
||||
type: 'game_start',
|
||||
your_color: 'black'
|
||||
});
|
||||
}, 5678);
|
||||
}
|
||||
|
||||
function joinServer() {
|
||||
gameState = 'searching';
|
||||
updateTitle();
|
||||
|
||||
function contact_fn(actor, reason) {
|
||||
console.log("CONTACTED!")
|
||||
if (actor) {
|
||||
opponent = actor;
|
||||
gameState = 'connected';
|
||||
myColor = 'black';
|
||||
isMyTurn = false;
|
||||
updateTitle();
|
||||
} 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 === 'quit')
|
||||
os.exit()
|
||||
else
|
||||
console.log(json.encode(e))
|
||||
})
|
||||
if (e.type === 'quit') os.exit()
|
||||
if (e.type === 'game_start') {
|
||||
myColor = e.your_color;
|
||||
isMyTurn = (myColor === 'white');
|
||||
updateTitle();
|
||||
} else if (e.type === 'move') {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prosperon.dispatch(e.type, e)
|
||||
})
|
||||
Reference in New Issue
Block a user