# globals $table = null table_height = 0 card_height = 0 state = null server_url = null top_card_z = 0 # css z-index of front-most card $pile_captions = {} show_message = (txt) -> txt # FIXME implement chat box or something # timeout function with args in convenient order timeout = (ms, func) -> setTimeout func, ms unless Array::shuffle? Array::shuffle = -> return if @length is 0 top = @length while --top current = Math.floor(Math.random() * (top + 1)) tmp = @[current] @[current] = @[top] @[top] = tmp return new_button = (text) -> $ $ "
#{text}
" # transform coordinates from client-side coords to server-side coords (or back) # this makes it so p2 view everything upside down (mirrored), but still sends coords rightside up flip_y = (y) -> table_height - card_height - y transform_x = (x) -> x transform_y = (y) -> return y unless state.agent is 'p2' return flip_y y next_card_z = -> return top_card_z += 1 bring_card_to_front = (card) -> card.view.css "z-index": next_card_z() new_blank_card = (x, y) -> view = $ $ "
" $table.append view return view instantiate_card = (card) -> text = card.text if card.owner is state.agent card_class = 'my_card' else card_class = 'your_card' # initial card state from server has z so that stacks come out with the right layers if card.z? if card.z > top_card_z top_card_z = card.z else unless card.pile card.z = next_card_z() view = $ $ "
#{text}
" view.data card.number button_box = $ $ '
' flip_button = new_button "flip over" mark_button = new_button "mark" flip_button.bind 'click', -> state.flip state.agent, card.number, ! view.hasClass 'flipped' mark_button.bind 'click', -> state.mark state.agent, card.number, ! view.hasClass 'marked' button_box.append flip_button button_box.append mark_button view.append button_box if card.marked view.addClass 'marked' if card.flipped view.addClass 'flipped' $table.append view view.draggable containment: '#table', grid: [20, 20] view.bind 'dragstart', (event, ui) -> view.css 'z-index': card.z = next_card_z() view.bind 'dragstop', (event, ui) -> p = view.position() state.move state.agent, card.number, transform_x(p.left), transform_y(p.top), card.z card.view = view error_lag = 3 outgoing_messages = [] # message should be [agent, method, args...] # don't forget the agent (state.agent) tell_server = (message) -> outgoing_messages.push message send_updates() send_updates = -> return if outgoing_messages.length is 0 messages = outgoing_messages outgoing_messages = [] $.ajax "#{server_url}/set", { cache: false data: { agent: state.agent game: 'test' # FIXME, and it the /get call too messages: JSON.stringify(messages) } type: 'POST' dataType: 'json' error: (xhr, status, error) -> show_message "Network error while sending, you might want to refresh. Trying again in #{error_lag} seconds. (Status: #{status}, Error: #{error})" for message in messages outgoing_messages.unshift message timeout error_lag * 1000, send_updates error_lag *= 2 success: (data, status, xhr) -> show_message "update sent" error_lag = 3 } error_lag = 3 poll_for_updates = -> $.ajax "#{server_url}/get?agent=#{state.agent}&game=test", { cache: false type: 'GET' dataType: 'json' error: (xhr, status, error) -> message "Network error, you might want to refresh. Trying again in #{error_lag} seconds. (Status: #{status}, Error: #{error})" timeout error_lag * 1000, poll_for_updates error_lag *= 2 success: (data, status, xhr) -> state.process_messages data timeout 100, poll_for_updates error_lag = 3 } n_cards = (count) -> return "#{count} cards" unless count is 1 return "1 card" initialize_cards = () -> # clear everything $('.card').remove() $('.blank_card').remove() # instantiate cards in play for card in state.cards instantiate_card card unless card.pile # build piles piles = [ {key: 'p2_draw', x: transform_x(140), y: transform_y(20), name: "Draw Pile"} {key: 'p2_discard', x: transform_x(20), y: transform_y(20), name: "Discard Pile"} {key: 'p1_draw', x: transform_x(140), y: transform_y(flip_y(20)), name: "Draw Pile"} {key: 'p1_discard', x: transform_x(20), y: transform_y(flip_y(20)), name: "Discard Pile"} ] for pile in piles manage_pile = (pile) -> pile.$blank = new_blank_card pile.x, pile.y count = 0 top = null if state.piles[pile.key]?.length count = state.piles[pile.key].length top = state.piles[pile.key][0] $caption = $ $ "
#{pile.name}:
#{n_cards count}
" pile.$caption = $caption if top? top.x = pile.x top.y = pile.y instantiate_card top view = top.view else view = pile.$blank view.append $caption if top? pile.drag_handler = view.bind 'dragstart', -> pile.$caption.remove() delete top.pile state.piles[pile.key].shift() manage_pile pile # FIXME make sure this state change is sent # FIXME handle this message coming in manage_pile pile init = -> if window.location.hash? and window.location.hash.length > 0 me = window.location.hash.substr 1 winloc = "#{window.location}" server_url = winloc.substr 0, winloc.length - window.location.hash.length else me = 'p1' server_url = window.location state = window.game_model.new me state.on 'move', (agent, card, x, y, z) -> # FIXME add/handle pile argument if agent is me tell_server ['move', agent, card, x, y, z] else # FIXME should we use the z from the server? Should p1 use odd numbers and p2 even? bring_card_to_front state.cards[card] state.cards[card].view.animate { left: "#{transform_x x}px", top: "#{transform_y y}px"}, 800 state.on 'mark', (agent, card, state) -> @cards[card].view.toggleClass 'marked', state if agent is me tell_server ['mark', agent, card, state] state.on 'flip', (agent, card, state) -> @cards[card].view.toggleClass 'flipped', state if agent is me tell_server ['flip', agent, card, state] state.on 'set_cards', (agent, cards) -> initialize_cards() if agent is me tell_server ['set_cards', agent, cards] # timeout so browser will stop showing that we're loading timeout 1, poll_for_updates timeout 2, -> # ask for initial state tell_server ['send_state', state.agent] $ -> $table = $ '#table' table_height = $table.height() card_height = $('#loading-card').outerHeight() init()