1 require 'observer_utils'
3 require 'board/pool_animator'
5 require 'interaction/match'
13 attr_reader :controlled
24 @field = AnimationField.new(20)
28 yield @board if @board
29 @pools.each {|c, pool| yield pool }
30 @clocks.each {|c, clock| yield clock }
36 move = @match.game.policy.new_move(state, @board.selection, p)
37 validate = @match.game.validator.new(state)
42 @board.selection = nil
43 elsif @match.game.policy.movable?(state, p) and movable?(p)
51 @board = @table.elements[:board]
52 @pools = @table.elements[:pools]
53 @clocks = @table.elements[:clocks]
55 @animator = @match.game.animator.new(@board)
56 @board.reset(match.state.board)
59 @clocks.each do |col, clock|
63 @board.observe(:click) {|p| on_board_click(p) }
64 @board.observe(:drag) {|data| on_board_drag(data) }
65 @board.observe(:drop) {|data| on_board_drop(data) }
66 @pools.each do |col, pool|
67 pool.observe(:drag) {|data| on_pool_drag(col, data) }
68 pool.observe(:drop) {|data| on_pool_drop(col, data) }
70 @clocks.each do |col, clock|
71 clock.data = { :color => col,
72 :player => match.player(col).name }
74 @match.observe(:move) do |data|
75 unless @controlled[data[:player].color] == data[:player]
76 animate(:forward, data[:state], data[:move])
77 @board.highlight(data[:move])
78 @clocks[data[:old_state].turn].stop
79 @clocks[data[:state].turn].start
83 @clocks[@match.game.players.first].active = true
84 @table.flip(@color != @match.game.players.first)
86 if @match.history.move
87 @board.highlight(@match.history.move)
91 def perform!(move, opts = {})
92 col = @match.state.turn
93 if @controlled[col] and @match.move(@controlled[col], move)
94 animate(:forward, @match.state, move, opts)
95 @board.highlight(move)
98 @clocks[@match.state.turn].start
103 state, move = @match.history.back
104 animate(:back, state, move)
105 @board.highlight(@match.history.move)
106 rescue History::OutOfBound
107 puts "error: first move"
111 state, move = @match.history.forward
112 animate(:forward, state, move)
113 @board.highlight(move)
114 rescue History::OutOfBound
115 puts "error: last move"
119 cur = @match.history.current
120 state, move = @match.history.go_to(index)
122 (cur + 1..index).each do |i|
123 animate(:forward, @match.history[i].state, @match.history[i].move)
125 @board.highlight(move)
127 (cur).downto(index + 1).each do |i|
128 animate(:back, @match.history[i - 1].state, @match.history[i].move)
130 @board.highlight(@match.history.move)
132 rescue History::OutOfBound
133 puts "error: no such index #{index}"
136 def animate(direction, state, move, opts = {})
137 anim = @animator.send(direction, state, move, opts)
144 @pools.each do |col, pool|
145 anim = pool.animator.warp(@match.state.pool(col))
150 def on_board_drop(data)
154 if data[:src] == data[:dst]
155 @board.selection = data[:src]
158 move = @match.game.policy.new_move(
159 @match.state, data[:src], data[:dst])
160 validate = @match.game.validator.new(@match.state)
164 if move and move.valid?
165 @board.add_to_group data[:item]
166 @board.lower data[:item]
167 perform! move, :adjust => true
171 elsif data[:index] and data[:dst]
173 move = @match.game.policy.new_move(
174 @match.state, nil, data[:dst],
175 :dropped => data[:item].name)
176 validate = @match.game.validator.new(@match.state)
178 @board.add_to_group data[:item]
179 @board.lower data[:item]
180 perform! move, :dropped => data[:item]
187 def on_board_drag(data)
188 if @match.game.policy.movable?(@match.state, data[:src]) and
190 @board.raise data[:item]
191 @board.remove_from_group data[:item]
192 @board.selection = nil
197 def on_pool_drag(c, data)
198 if @match.game.policy.droppable?(@match.state, c, data[:index]) and
199 droppable?(c, data[:index])
202 # replace item with a correctly sized one
203 item = @board.create_piece(data[:item].name)
205 @board.remove_from_group item
206 anim = @pools[c].animator.remove_piece(data[:index])
208 data[:size] = @board.unit
209 data[:pool_color] = c
217 def on_pool_drop(color, data)
221 def cancel_drop(data)
222 anim = if data[:index]
223 # remove dragged item
225 # make original item reappear in its place
226 @pools[data[:pool_color]].animator.insert_piece(
230 @board.add_to_group data[:item]
231 @board.lower data[:item]
232 @animator.movement(data[:item], nil, data[:src], Path::Linear)
235 @field.run(anim) if anim
239 time.each do |pl, seconds|
240 @clocks[pl].clock ||= Clock.new(seconds, 0, nil)
241 @clocks[pl].clock.set_time(seconds)
247 @clocks.each do |pl, clock|
253 def add_controlled_player(player)
254 @controlled[player.color] = player
259 @controlled = { @color => self }
266 def droppable?(color, index)
273 return false unless @controlled[@match.state.turn]
274 if @match.history.current < @match.index