### Peach CGT -- Card Game Table simulator Copyright (C) 2011 Jason Woofenden Lincensed under AGPLv3. Source here: https://gitorious.org/peach-cgt ### # globals $table = null table_width = 0 table_height = 0 card_width = 0 card_height = 0 state = null server_url = null top_card_z = 0 # css z-index of front-most card piles = null window.log = [] show_message = (txt) -> window.log.push txt if window.log.length > 20 window.log.shift() return # 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_x = (x) -> table_width - card_width - x flip_y = (y) -> table_height - card_height - y transform_x = (x) -> return x unless state.agent is 'p2' return flip_x x transform_y = (y) -> return y unless state.agent is 'p2' return flip_y y next_card_z = -> top_card_z += 1 # p1 gets even numbers, p2 gets odd numbers if state.agent is 'p1' top_card_z += top_card_z % 2 else top_card_z += 1 - (top_card_z % 2) show_message "new z: #{top_card_z}" return top_card_z new_blank_card = (x, y, css_class) -> view = $ $ "
" $table.append view return view find_pile = (x, y) -> fudge = 40 for pile in piles if -fudge < pile.x - x < fudge and -fudge < pile.y - y < fudge return pile return null in_your_hand = (card) -> return (not (card.pile?)) and ((transform_y card.y) < (card_height * 0.8)) uninstantiate_card = (card) -> show_message "uninstantiate card #{card.number}" card.view.remove() delete card.view instantiate_card = (card) -> show_message "instantiate card #{card.number}" if card.view die.a.horrible.death() text = card.text if card.owner is state.agent card_class = 'my_card' else card_class = 'your_card' if in_your_hand card card_class = "#{card_class} your_hand" if card.z > top_card_z top_card_z = card.z view = $ $ "
#{text}
" card.view = view 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 grid: [20, 20] view.bind 'dragstart', (event, ui) -> view.css 'z-index': card.z = next_card_z() if card.pile? delete card.pile update_pile_views() view.bind 'dragstop', (event, ui) -> p = view.position() x = transform_x(p.left) y = transform_y(p.top) pile = find_pile x, y if pile? x = pile.x y = pile.y pile = pile.key view.css {left: transform_x(x), top: transform_y(y)} state.move state.agent, card.number, x, y, card.z, pile 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 = [] show_message "#{server_url}set" $.ajax "#{server_url}set", { cache: false data: { agent: state.agent game: state.slug 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=#{state.slug}", { cache: false type: 'GET' dataType: 'json' error: (xhr, status, error) -> show_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 = () -> show_message 'initialize_cards' $('.card').remove() top_card_z = 0 # instantiate cards in play hide_deck_designer = false for card in state.cards if card.owner is state.agent hide_deck_designer = true delete card.view if hide_deck_designer $('#deck_designer').remove() unless piles? piles = [ # global {key: 'p2_draw', x: 140, y: 20, name: "Draw Pile"} {key: 'p2_discard', x: 20, y: 20, name: "Discard Pile"} {key: 'p1_draw', x: flip_x(140), y: flip_y(20), name: "Draw Pile"} {key: 'p1_discard', x: flip_x(20), y: flip_y(20), name: "Discard Pile"} ] for pile in piles if pile.key.substr(0, 2) is state.agent css_class = 'my_card' else css_class = 'your_card' pile.$blank = new_blank_card pile.x, pile.y, css_class pile.$caption = $ $ "
#{pile.name}:
#{n_cards 0}
" update_pile_views() # also makes sure all non-piled cards are instantiated update_pile_views = -> ps = {} for card in state.cards if card.pile? if ps[card.pile]? ps[card.pile].total += 1 if card.z > ps[card.pile].top_z if ps[card.pile].top_card.view? uninstantiate_card ps[card.pile].top_card ps[card.pile].top_card = card ps[card.pile].top_z = card.z else if card.view uninstantiate_card card else ps[card.pile] = { total: 1, top_card: card, top_z: card.z } else # not in a pile instantiate_card card unless card.view? for pile in piles # where should the caption be? if ps[pile.key]? unless ps[pile.key].top_card.view? ps[pile.key].top_card.x = pile.x ps[pile.key].top_card.y = pile.y instantiate_card ps[pile.key].top_card caption_dest = ps[pile.key].top_card.view else caption_dest = pile.$blank if caption_dest isnt pile.caption_loc pile.$caption.detach() caption_dest.append pile.$caption pile.caption_loc = caption_dest # update caption to show correct number of cards in the pile card_count = 0 card_count = ps[pile.key].total if ps[pile.key]? pile.$caption.children('.n_cards').html n_cards card_count possible_cards = {} valumenous = (val) -> return true unless val is '' or val is ' ' init_possible_cards = -> for card in window.cs_cards text = "#{card.cardname} (#{card.faction})" if valumenous card.attack or valumenous card.defense text += " #{card.attack}/#{card.defense}" text += "
#{card.type}" if valumenous card.subtype text += " • #{card.subtype}" text += "
cost: #{card.cost} thresh: #{card.threshold}
" text += card.rules summary = text.replace(/
/g, "\n") possible_cards[card.id] = {id: card.id, text: text, summary: summary} init_card_designer = -> show_message 'init_card_designer' cards_in_deck = {} container = $ '#deck_designer' init_possible_cards() ul = $ $ '