Skip to content

Commit

Permalink
Merge pull request #34 from carbonfive/feature/display-current-player…
Browse files Browse the repository at this point in the history
…-information-4

Display game and player info in a status bar.
  • Loading branch information
Rudy Jahchan committed Jan 5, 2014
2 parents 386fdc0 + cb0cf05 commit 24edf42
Show file tree
Hide file tree
Showing 16 changed files with 413 additions and 125 deletions.
13 changes: 9 additions & 4 deletions src/client/index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ class Client
process.stdin.setRawMode true
process.stdin.resume()
process.stdin.on 'data', @onData
@socket.on 'game', @onGameUpdate
@socket.on 'error', @showErrorMessage
@socket.emit 'join', @gameAttributes
@socket.emit 'join', @gameAttributes, (error, cycleNumber, game) =>
return @showErrorMessage(error.message) if error?
@cycleNumber = cycleNumber
@onGameUpdate(game)
@socket.on 'game', @onGameUpdate

onData: (chunk)=>
switch chunk[0]
Expand All @@ -47,7 +49,8 @@ class Client
process.nextTick process.exit

onGameUpdate: (game)=>
@gameView.setGame(game)
@gameView.game = game
@gameView.cycleNumber = @cycleNumber
@gameView.render()

andListGames: =>
Expand All @@ -64,4 +67,6 @@ class Client
console.log message
@quit()

storeCycle: (cycle)=> @cycle = cycle

module.exports = Client
31 changes: 28 additions & 3 deletions src/client/screen.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,34 @@ exports.showCursor = ->
exports.setForegroundColor = (color)->
process.stdout.write "\x1b[3#{color}m"

exports.setBackgroundColor = (color)->
process.stdout.write "\x1b[4#{color}m"

exports.moveTo = (x, y) ->
process.stdout.write "\x1b[#{y};#{x}f"

exports.center = ->
width = process.stdout.columns
width/2
exports.resetColors = ->
process.stdout.write '\x1b[39;49m'

exports.render = (buffer)->
process.stdout.write buffer

[ LEFT, RIGHT, CENTER ] = [0, 1, 2]

exports.TEXT_ALIGN = { LEFT, RIGHT, CENTER }

exports.print = (string, x, y, alignment=LEFT) ->
[sx, sy] = switch alignment
when LEFT then [x, y]
when RIGHT then [x - string.length + 1, y]
when CENTER then [x - string.length/2, y]
exports.moveTo sx, sy
exports.render string

Object.defineProperty exports, 'columns', get: -> process.stdout.columns
Object.defineProperty exports, 'rows', get: -> process.stdout.rows
Object.defineProperty exports, 'center', get: -> Math.round(@columns/2)
Object.defineProperty exports, 'maxGridRows', get: -> process.stdout.rows - 4
Object.defineProperty exports, 'maxGridColumns', get: -> process.stdout.columns - 2
Object.defineProperty exports, 'maxGridSize',
get: -> Math.min @maxGridRows, @maxGridColumns
9 changes: 4 additions & 5 deletions src/client/views/cycle_view.coffee
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require '../../define_property'

directions = require '../../models/directions'
buffer = require '../buffer'
screen = require '../screen'
Expand Down Expand Up @@ -46,9 +48,6 @@ class CycleView

@renderWallViews()

if @game.state == Game.STATES.COUNTDOWN
@renderName()

if @cycle.state == Cycle.STATES.WINNER
@renderWinnerMessage()

Expand All @@ -62,11 +61,11 @@ class CycleView
screen.moveTo(@nameX, @nameY)
process.stdout.write "Player #{@cycle.number}"

_nameX: =>
@property 'nameX', =>
screenX = @startX + @cycle.x
if @cycle.x > 25 then screenX - 10 else screenX + 5

_nameY: => @cycle.y + 1
@property 'nameY', => @cycle.y + 1

renderWinnerMessage: ->
messageX = @startX + @cycle.x - 1
Expand Down
77 changes: 49 additions & 28 deletions src/client/views/game_view.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
require '../../define_property.coffee'

screen = require '../screen'
buffer = require '../buffer'
Game = require '../../models/game'
Cycle = require '../../models/cycle'
CycleView = require './cycle_view'
playerColors = require './player_colors'

ARENA_WALL_CHARS = {
HORIZONTAL: buffer(0xE2, 0x95, 0x90)
Expand All @@ -18,20 +22,26 @@ class GameView
@cycleViews = []
@countString = ''

Object.defineProperty @, 'state', get: @_state

_state: => @game?.state

setGame: (game)->
@game = game
@startX = Math.round(screen.center() - (@game.gridSize/2))
if @game.count != @lastCount && @state == Game.STATES.COUNTDOWN
@lastCount = @game.count
@countString += "#{@game.count}..."
@generateCycleViews()
@property 'state', get: -> @_game?.state
@property 'game', {
set: (game)->
@_game = game
@startX = Math.round(screen.center - (@_game.gridSize/2))
if @_game.count != @lastCount and @state == Game.STATES.COUNTDOWN
@lastCount = @_game.count
@countString += "#{@_game.count}..."
@generateCycleViews()
get: -> @_game
}
@property 'stateString', get: ->
switch @_game?.state
when Game.STATES.WAITING then 'Waiting for other players'
when Game.STATES.COUNTDOWN then 'Get ready'
when Game.STATES.STARTED then 'Go'
when Game.STATES.FINISHED then 'Game over'

generateCycleViews: ->
@cycleViews = (new CycleView(cycle, @game, @startX) for cycle in @game.cycles)
@cycleViews = (new CycleView(cycle, @_game, @startX) for cycle in @_game.cycles)

render: ->
screen.clear()
Expand All @@ -42,40 +52,37 @@ class GameView
else
@renderArena()
@renderCycleViews()
if @game? and @cycleNumber? then @renderGameInfo()

renderArena: ->
screen.setForegroundColor 3
xRange = @game.gridSize - 2
xRange = @game.gridSize - 1
yRange = @game.gridSize - 1
endX = @startX + @game.gridSize
endY = @game.gridSize
screen.moveTo(@startX,1)
process.stdout.write ARENA_WALL_CHARS.TOP_LEFT_CORNER
screen.render ARENA_WALL_CHARS.TOP_LEFT_CORNER
for x in [1..xRange]
screen.moveTo (@startX + x), 1
process.stdout.write ARENA_WALL_CHARS.HORIZONTAL
screen.render ARENA_WALL_CHARS.HORIZONTAL
screen.moveTo endX, 1
process.stdout.write ARENA_WALL_CHARS.TOP_RIGHT_CORNER
screen.render ARENA_WALL_CHARS.TOP_RIGHT_CORNER
for y in [2..yRange]
screen.moveTo endX, y
process.stdout.write ARENA_WALL_CHARS.VERTICAL
screen.render ARENA_WALL_CHARS.VERTICAL
screen.moveTo endX, endY
process.stdout.write ARENA_WALL_CHARS.BOTTOM_RIGHT_CORNER
screen.render ARENA_WALL_CHARS.BOTTOM_RIGHT_CORNER
for x in [xRange..1]
screen.moveTo (@startX + x), endY
process.stdout.write ARENA_WALL_CHARS.HORIZONTAL
screen.render ARENA_WALL_CHARS.HORIZONTAL
screen.moveTo @startX, endY
process.stdout.write ARENA_WALL_CHARS.BOTTOM_LEFT_CORNER
screen.render ARENA_WALL_CHARS.BOTTOM_LEFT_CORNER
for y in [yRange..2]
screen.moveTo @startX, y
process.stdout.write ARENA_WALL_CHARS.VERTICAL
screen.render ARENA_WALL_CHARS.VERTICAL

renderWaitScreen: ->
@renderArena()
screen.setForegroundColor 3
messageX = @startX + 12
screen.moveTo(messageX, 25)
process.stdout.write 'Waiting for other players...'

renderCountdown: ->
@renderArena()
Expand All @@ -84,11 +91,25 @@ class GameView

renderCount: ->
screen.setForegroundColor 3
countX = @startX + 20
screen.moveTo(countX,25)
process.stdout.write @countString
countX = @startX + Math.round(@game.gridSize/2)
screen.print @countString, countX, Math.round(@game.gridSize/2), screen.TEXT_ALIGN.CENTER

renderCycleViews: ->
cycleView.render() for cycleView in @cycleViews

renderGameInfo: ->
if @game.name? and @cycleNumber?
screen.setBackgroundColor playerColors(@cycleNumber)
screen.setForegroundColor 0
screen.print((' ' for i in [1..screen.columns]).join(''), 1, screen.rows - 1)
screen.print("#{@game.name} Player: #{@cycleNumber} State: #{@stateString}", 1, screen.rows - 1)
screen.resetColors()
if @playerCycle.state == 4
screen.setForegroundColor playerColors(@cycleNumber)
screen.print('-- INSERT --', 1, screen.rows)
screen.resetColors()

@property 'playerCycle', get: ->
(cycle for cycle in @_game.cycles when cycle.number == @cycleNumber).pop()

module.exports = GameView
12 changes: 12 additions & 0 deletions src/client/views/player_colors.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
colors = {
1: 7
2: 2
3: 3
4: 5
5: 6
6: 7
7: 4
8: 1
}

module.exports = (cycleNumber)-> colors[cycleNumber]
2 changes: 2 additions & 0 deletions src/define_property.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Function::property = (property, description)->
Object.defineProperty @prototype, property, description
8 changes: 6 additions & 2 deletions src/models/game.coffee
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require '../define_property'

{ EventEmitter } = require('events')
directions = require './directions'
playerAttributes = require './player_attributes'
Expand All @@ -21,11 +23,14 @@ class Game extends EventEmitter
@count = 3

addCycle: ->
return null if @inProgress

attributes = playerAttributes[@cycles.length]
attributes['x'] = @playerPositions[@cycles.length]['x']
attributes['y'] = @playerPositions[@cycles.length]['y']
attributes['game'] = @
cycle = new Cycle(attributes)

@cycles.push cycle
if @activeCycleCount() == @numberOfPlayers
@start()
Expand Down Expand Up @@ -76,8 +81,7 @@ class Game extends EventEmitter
determineWinner: ->
cycle.makeWinner() for cycle in @cycles when cycle.state != Cycle.STATES.DEAD

inProgress: ->
@state != Game.STATES.WAITING
@property 'inProgress', get: -> @state != Game.STATES.WAITING

calculatePlayerPositions: ->
minDistance = 3
Expand Down
15 changes: 15 additions & 0 deletions src/server/client_game_socket.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class ClientGameSocket
constructor: (@socket, @game, @cycle)->
@socket.on 'movement', @onMovement
@socket.on 'disconnect', @onLeave
@socket.on 'leave', @onLeave
@socket.join @game.name
@socket.emit 'cycle', @cycle

onLeave: =>
@socket.leave @game.name
@game.removeCycle @cycle

onMovement: (movement)=> @cycle.navigate(movement)

module.exports = ClientGameSocket
23 changes: 23 additions & 0 deletions src/server/client_socket.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
ClientGameSocket = require './client_game_socket'

class ClientSocket
constructor: (@socket, @server, @gameSocketFactory)->
@gameSocketFactory ?= (socket, game, cycle)->
new ClientGameSocket(socket, game, cycle)
@socket.on 'join', @onJoin
@socket.on 'list', @onList

onJoin: (gameAttributes, callback=(error, cycle)->)=>
game = @server.getGame(gameAttributes)
if game?
if (cycle = game.addCycle())?
@gameSocketFactory(@socket, game, cycle)
callback null, cycle.number, game.toJSON()
else
callback message: "Game '#{gameAttributes.name}' is already in progress."
else
callback message: "Could not find or create game named '#{gameAttributes.name}'."

onList: => @socket.emit 'games', @server.gameList()

module.exports = ClientSocket
45 changes: 8 additions & 37 deletions src/server/index.coffee
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
Game = require '../models/game'
http = require 'http'
socketio = require 'socket.io'
ClientSocket = require './client_socket'

createGame = (attributes, server)->
game = new Game(attributes)
game.addListener 'game', server.onGameChange
game.addListener 'stopped', server.onGameStopped
game

class Server
constructor: ()->
@games = {}

getGame: (attributes) ->
game = @games[attributes.name] = (@games[attributes.name] ? new Game(attributes))
return null if game.inProgress()
game.addListener 'game', @onGameChange
game.addListener 'stopped', @onGameStopped
game
@games[attributes.name] = (@games[attributes.name] ? createGame(attributes, @))

gameList: ->
(game.toJSON() for name, game of @games)
Expand Down Expand Up @@ -41,36 +44,4 @@ class Server

close: (cb=(->))-> @server?.close cb

class ClientSocket
constructor: (@socket, @server)->
@socket.on 'movement', @onMovement
@socket.on 'disconnect', @onDisconnect
@socket.on 'join', @onJoin
@socket.on 'leave', @onLeave
@socket.on 'list', @onList

onJoin: (properties)=>
@game = @server.getGame(properties)
if @game?
@socket.join @game.name
@cycle = @game.addCycle()
else
@socket.emit('error', "Game '#{name}' is already in progress.")

onLeave: =>
if @game?
@socket.leave @game.name
@game.removeCycle @cycle
@cycle = null
@game = null

onMovement: (movement)=>
@cycle.navigate(movement) if @game?

onDisconnect: =>
@onLeave()

onList: =>
@socket.emit 'games', @server.gameList()

module.exports = Server
Loading

0 comments on commit 24edf42

Please sign in to comment.