diff --git a/example/ping b/example/ping new file mode 100755 index 0000000..cc505f4 --- /dev/null +++ b/example/ping @@ -0,0 +1,439 @@ +#!/usr/bin/env node + +/** + * ping + * https://github.com/chjj/blessed + * Copyright (c) 2013, Christopher Jeffrey (MIT License) + * Online (ping)pong in your terminal. + */ + +// Example Usage: +// Server: $ ./example/ping 3000 +// Client: $ ./example/ping 127.0.0.1 3000 +// Demo: $ ./example/ping + +process.title = 'ping'; + +if (/^(-h|--help|-\?)$/.test(process.argv[2])) { + console.log('node-ping'); + console.log('Example Usage:'); + console.log('Server: $ node-ping 3000'); + console.log('Client: $ node-ping 127.0.0.1 3000'); + console.log('Demo: $ node-ping'); + return process.exit(0); +} + +var blessed = require('blessed') + , program = blessed() + , nssocket; + +try { + nssocket = require('nssocket'); +} catch (e) { + ; +} + +var server + , socket; + +/** + * Screen Layout + */ + +var screen = new blessed.Screen({ + program: program +}); + +var table = new blessed.Box({ + left: 0, + top: 0, + width: screen.width, + height: screen.height +}); + +var ball = new blessed.Box({ + width: 1, + height: 1, + bg: 'white', + top: 0, + left: 0 +}); + +var lpaddle = new blessed.Box({ + width: 1, + height: 3, + bg: 'yellow', + top: 0, + left: 0 +}); + +var rpaddle = new blessed.Box({ + width: 1, + height: 3, + bg: 'yellow', + top: 0, + right: 0 +}); + +var score = new blessed.Box({ + top: 0, + left: 4, + height: 3, + width: 20, + bold: true +}); + +score.lwins = 0; +score.rwins = 0; + +var net = new blessed.Box({ + width: 1, + height: '100%', + bg: 'yellow', + top: 0, + left: 'center' +}); + +var message = new blessed.Box({ + width: '50%', + height: 3, + border: { + type: 'ascii' + }, + top: 'center', + left: 'center' +}); + +var text = new blessed.Box({ + top: 'center', + left: 1, + right: 1, + height: 1, + content: 'Waiting for players to connect...' +}); + +message.append(text); + +screen.append(table); + +table.append(score); +table.append(lpaddle); +table.append(rpaddle); +table.append(net); +table.append(ball); +table.append(message); + +screen.on('resize', function() { + table.width = screen.width; + table.height = screen.height; + [ball, lpaddle, rpaddle].forEach(function(el) { + if (el.rbottom < 0) el.rtop = table.height - 1 - el.height; + if (el.rright < 0) el.rleft = table.width - 1; + }); + screen.render(); + sync(); +}); + +/** + * Options + */ + +ball.speed = 2; +ball.unpredictable = true; +lpaddle.speed = 2; +rpaddle.speed = 2; + +/** + * Game + */ + +function sync() { + if (!socket) return; + socket.send(['update'], { + table: { width: table.width, height: table.height }, + lpaddle: { rleft: lpaddle.rleft, rtop: lpaddle.rtop, speed: lpaddle.speed }, + rpaddle: { rleft: rpaddle.rleft, rtop: rpaddle.rtop, speed: rpaddle.speed }, + score: { lwins: score.lwins, rwins: score.rwins } + }); +} + +function reset() { + text.setContent('Waiting for players to connect...'); + message.hide(); + ball.moving = true; + ball.direction = 'right'; + ball.angle = 'down'; + ball.rtop = 1; + ball.rleft = 1; + lpaddle.rtop = 0; + rpaddle.rtop = 0; + + score.setContent('Score: ' + score.lwins + ' | ' + score.rwins); + + rpaddle.movable = true; + + screen.render(); + if (server && socket) { + socket.send(['reset']); + } +} + +function rand(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function startGame() { + reset(); + + if (startGame._bound) return; + startGame._bound = true; + + screen.on('keypress', function(ch, key) { + if (!ball.moving) return; + if (key.name === 'up' || key.name === 'k') { + if (socket) socket.send(['up']); + if (lpaddle.rtop > 0) lpaddle.rtop -= lpaddle.speed; + if (!socket) if (rpaddle.rtop > 0) rpaddle.rtop -= rpaddle.speed; + if (lpaddle.rtop < 0) lpaddle.rtop = 0; + if (rpaddle.rtop < 0) rpaddle.rtop = 0; + screen.render(); + } else if (key.name === 'down' || key.name === 'j') { + if (socket) socket.send(['down']); + if (lpaddle.rbottom > 0) lpaddle.rtop += lpaddle.speed; + if (!socket) if (rpaddle.rbottom > 0) rpaddle.rtop += rpaddle.speed; + if (lpaddle.rbottom < 0) lpaddle.rtop = table.height - lpaddle.height - 1; + if (rpaddle.rbottom < 0) rpaddle.rbottom = table.height - rpaddle.height - 1; + screen.render(); + } + }); + + setInterval(function() { + if (!ball.moving) return; + if (ball.direction === 'right') { + if (ball.rright > 1) { + ball.rleft += ball.speed; + } else { + if (ball.rtop >= rpaddle.rtop && ball.rtop <= rpaddle.rtop + rpaddle.height) { + ball.direction = 'left'; + ball.rleft -= ball.speed; + + ball.rleft -= rand(0, 3); + if (ball.angle === 'down') ball.rtop += rand(0, 3); + else if (ball.angle === 'up') ball.rtop -= rand(0, 3); + } else { + // Right loses + score.lwins++; + ball.rleft = table.width - 1; + if (socket) socket.send(['lose']); + ball.moving = false; + text.setContent('Right player loses!'); + message.show(); + setTimeout(reset, 3000); + screen.render(); + return; + } + } + if (ball.rright < 1) ball.rleft = table.width - 2; + } else if (ball.direction === 'left') { + if (ball.rleft > 1) { + ball.rleft -= ball.speed; + } else { + if (ball.rtop >= lpaddle.rtop && ball.rtop <= lpaddle.rtop + lpaddle.height) { + ball.direction = 'right'; + ball.rleft += ball.speed; + + ball.rleft += rand(0, 3); + if (ball.angle === 'down') ball.rtop += rand(0, 3); + else if (ball.angle === 'up') ball.rtop -= rand(0, 3); + } else { + // Left loses + score.rwins++; + ball.rleft = 0; + if (socket) socket.send(['win']); + ball.moving = false; + text.setContent('Left player loses!'); + message.show(); + setTimeout(reset, 3000); + screen.render(); + return; + } + } + if (ball.rleft < 1) ball.rleft = 1; + } + if (ball.angle === 'down') { + if (ball.rbottom > 0) { + ball.rtop++; + if (ball.unpredictable) ball.rtop += rand(0, 3); + } else { + ball.angle = 'up'; + ball.rtop--; + } + } else if (ball.angle === 'up') { + if (ball.rtop > 0) { + ball.rtop--; + if (ball.unpredictable) ball.rtop -= rand(0, 3); + } else { + ball.angle = 'down'; + ball.rtop++; + } + } + if (ball.rtop < 0) ball.rtop = 0; + if (ball.rbottom < 0) ball.rtop = table.height - 1; + if (socket) socket.send(['ball'], { rleft: ball.rleft, rtop: ball.rtop }); + screen.render(); + }, 100); +} + +function startServer() { + server = nssocket.createServer({}, function(socket_) { + socket = socket_; + + sync(); + + socket.data(['up'], function() { + if (!ball.moving) return; + if (rpaddle.rtop > 0) rpaddle.rtop -= rpaddle.speed; + screen.render(); + }); + + socket.data(['down'], function() { + if (!ball.moving) return; + if (rpaddle.rtop < table.height - 1) rpaddle.rtop += rpaddle.speed; + screen.render(); + }); + + socket.on('error', function() { + socket = null; + reset(); + ball.moving = false; + message.show(); + screen.render(); + }); + + startGame(); + }); + + server.listen(+process.argv[2]); +} + +function startClient() { + var socket = new nssocket.NsSocket({ + reconnect: true, + maxRetries: Infinity, + retryInterval: 5000 + }); + + socket.connect(+process.argv[3], process.argv[2]); + + screen.on('keypress', function(ch, key) { + if (!rpaddle.movable) return; + if (key.name === 'up' || key.name === 'k') { + socket.send(['up']); + if (rpaddle.rtop > 0) rpaddle.rtop -= rpaddle.speed; + if (rpaddle.rtop < 0) rpaddle.rtop = 0; + screen.render(); + } else if (key.name === 'down' || key.name === 'j') { + socket.send(['down']); + if (rpaddle.rbottom > 0) rpaddle.rtop += rpaddle.speed; + if (rpaddle.rbottom < 0) rpaddle.rtop = table.height - rpaddle.height - 1; + screen.render(); + } + }); + + socket.data(['up'], function() { + if (lpaddle.rtop > 0) lpaddle.rtop -= lpaddle.speed; + screen.render(); + }); + + socket.data(['down'], function() { + if (lpaddle.rtop < table.height - 1) lpaddle.rtop += lpaddle.speed; + screen.render(); + }); + + socket.data(['ball'], function(data) { + ball.rleft = data.rleft; + ball.rtop = data.rtop; + screen.render(); + }); + + socket.data(['update'], function(data) { + if (data.lpaddle) { + lpaddle.rleft = data.lpaddle.rleft; + lpaddle.rtop = data.lpaddle.rtop; + lpaddle.speed = data.lpaddle.speed; + } + + if (data.rpaddle) { + rpaddle.rleft = data.rpaddle.rleft; + rpaddle.rtop = data.rpaddle.rtop; + rpaddle.speed = data.rpaddle.speed; + } + + if (data.ball) { + ball.moving = data.ball.moving; + ball.rleft = data.ball.rleft; + ball.rtop = data.ball.rtop; + } + + if (data.table) { + table.height = data.table.height; + table.width = data.table.width; + } + + if (data.score) { + score.lwins = data.score.lwins; + score.rwins = data.score.rwins; + } + + screen.render(); + }); + + socket.data(['win'], function() { + rpaddle.movable = false; + score.rwins++; + text.setContent('Left player loses!'); + message.show(); + screen.render(); + }); + + socket.data(['lose'], function() { + rpaddle.movable = false; + score.lwins++; + text.setContent('Right player loses!'); + message.show(); + screen.render(); + }); + + socket.data(['reset'], reset); + + reset(); +} + +/** + * Main + */ + +function main() { + screen.on('keypress', function(ch, key) { + if (key.name === 'q' || key.name === 'escape') { + return process.exit(0); + } + }); + + screen.render(); + + // Demo Mode / Single Player + if (!nssocket || !process.argv[2]) return startGame(); + + // Server Mode + if (!process.argv[3]) return startServer(); + + // Client Mode + if (process.argv[2] && process.argv[3]) return startClient(); +} + +/** + * Execute + */ + +main();