1 require 'observer_utils'
3 require 'board/pool_animator'
5 require 'interaction/match'
12 attr_reader :controlled
22 @field = AnimationField.new(20)
26 yield @board if @board
27 @pools.each {|c, pool| yield pool }
28 @clocks.each {|c, clock| yield clock }
34 move = @match.game.policy.new_move(state, @board.selection, p)
35 validate = @match.game.validator.new(state)
40 @board.selection = nil
41 elsif @match.game.policy.movable?(state, p) and movable?(p)
49 @board = @table.elements[:board]
50 @pools = @table.elements[:pools]
51 @clocks = @table.elements[:clocks]
53 @animator = @match.game.animator.new(@board)
54 @board.reset(match.state.board)
57 @clocks.each do |col, clock|
61 @board.observe(:click) {|p| on_board_click(p) }
62 @board.observe(:drag) {|data| on_board_drag(data) }
63 @board.observe(:drop) {|data| on_board_drop(data) }
64 @pools.each do |col, pool|
65 pool.observe(:drag) {|data| on_pool_drag(col, data) }
66 pool.observe(:drop) {|data| on_pool_drop(col, data) }
68 @clocks.each do |col, clock|
69 clock.clock = Clock.new(300, 0, nil)
70 clock.data = { :color => col }
72 @match.observe(:move) do |data|
73 unless @controlled[data[:player].color] == data[:player]
74 animate(:forward, data[:state], data[:move])
75 @board.highlight(data[:move])
76 @clocks[data[:old_state].turn].stop
77 @clocks[data[:state].turn].stop
81 @clocks[@match.game.players.first].active = true
82 @table.flip(@color != @match.game.players.first)
85 def perform!(move, opts = {})
86 col = @match.state.turn
87 if @controlled[col] and @match.move(@controlled[col], move)
88 animate(:forward, @match.state, move, opts)
89 @board.highlight(move)
92 @clocks[@match.state.turn].start
97 state, move = @match.history.back
98 animate(:back, state, move)
99 @board.highlight(@match.history.move)
100 rescue History::OutOfBound
101 puts "error: first move"
105 state, move = @match.history.forward
106 animate(:forward, state, move)
107 @board.highlight(move)
108 rescue History::OutOfBound
109 puts "error: last move"
113 cur = @match.history.current
114 state, move = @match.history.go_to(index)
116 (cur + 1..index).each do |i|
117 animate(:forward, @match.history[i].state, @match.history[i].move)
119 @board.highlight(move)
121 (cur).downto(index + 1).each do |i|
122 animate(:back, @match.history[i - 1].state, @match.history[i].move)
124 @board.highlight(@match.history.move)
126 rescue History::OutOfBound
127 puts "error: no such index #{index}"
130 def animate(direction, state, move, opts = {})
131 anim = @animator.send(direction, state, move, opts)
138 @pools.each do |col, pool|
139 anim = pool.animator.warp(@match.state.pool(col))
144 def on_board_drop(data)
148 if data[:src] == data[:dst]
149 @board.selection = data[:src]
152 move = @match.game.policy.new_move(
153 @match.state, data[:src], data[:dst])
154 validate = @match.game.validator.new(@match.state)
158 if move and move.valid?
159 @board.add_to_group data[:item]
160 @board.lower data[:item]
161 perform! move, :adjust => true
165 elsif data[:index] and data[:dst]
167 move = @match.game.policy.new_move(
168 @match.state, nil, data[:dst],
169 :dropped => data[:item].name)
170 validate = @match.game.validator.new(@match.state)
172 @board.add_to_group data[:item]
173 @board.lower data[:item]
174 perform! move, :dropped => data[:item]
181 def on_board_drag(data)
182 if @match.game.policy.movable?(@match.state, data[:src]) and
184 @board.raise data[:item]
185 @board.remove_from_group data[:item]
186 @board.selection = nil
191 def on_pool_drag(c, data)
192 if @match.game.policy.droppable?(@match.state, c, data[:index]) and
193 droppable?(c, data[:index])
196 # replace item with a correctly sized one
197 item = @board.create_piece(data[:item].name)
199 @board.remove_from_group item
200 anim = @pools[c].animator.remove_piece(data[:index])
202 data[:size] = @board.unit
203 data[:pool_color] = c
211 def on_pool_drop(color, data)
215 def cancel_drop(data)
216 anim = if data[:index]
217 # remove dragged item
219 # make original item reappear in its place
220 @pools[data[:pool_color]].animator.insert_piece(
224 @board.add_to_group data[:item]
225 @board.lower data[:item]
226 @animator.movement(data[:item], nil, data[:src], Path::Linear)
229 @field.run(anim) if anim
232 def add_controlled_player(player)
233 @controlled[player.color] = player
238 @controlled = { @color => self }
245 def droppable?(color, index)
252 return false unless @controlled[@match.state.turn]
253 if @match.history.current < @match.index