1 require 'observer_utils'
3 require 'board/pool_animator'
5 require 'interaction/match'
13 attr_reader :controlled
25 @field = AnimationField.new(20)
29 yield @board if @board
30 @pools.each {|c, pool| yield pool }
31 @clocks.each {|c, clock| yield clock }
35 state = @match.history.state
37 move = @policy.new_move(state, @board.selection, p)
38 validate = @match.game.validator.new(state)
43 @board.selection = nil
44 elsif @policy.movable?(state, p) and movable?(p)
51 @policy = match.game.policy.new
52 @current = match.history.current
55 @board = @table.elements[:board]
56 @pools = @table.elements[:pools]
57 @clocks = @table.elements[:clocks]
59 @animator = @match.game.animator.new(@board)
60 @board.reset(match.state.board)
63 @clocks.each do |col, clock|
67 @board.observe(:click) {|p| on_board_click(p) }
68 @board.observe(:drag) {|data| on_board_drag(data) }
69 @board.observe(:drop) {|data| on_board_drop(data) }
70 @pools.each do |col, pool|
71 pool.observe(:drag) {|data| on_pool_drag(col, data) }
72 pool.observe(:drop) {|data| on_pool_drop(col, data) }
74 @clocks.each do |col, clock|
75 clock.data = { :color => col,
76 :player => match.player(col).name }
79 @match.history.observe(:current_changed) { refresh }
81 @match.observe(:move) do |data|
83 @clocks[data[:old_state].turn].stop
84 @clocks[data[:state].turn].start
87 @clocks[@match.game.players.first].active = true
88 @table.flip(@color && (@color != @match.game.players.first))
90 if @match.history.move
91 @board.highlight(@match.history.move)
95 def perform!(move, opts = {})
96 turn = @match.history.state.turn
98 @next_refresh_opts = opts
99 @match.move(@controlled[turn], move, opts)
105 rescue History::OutOfBound
106 puts "error: first move"
110 @match.history.forward
111 rescue History::OutOfBound
112 puts "error: last move"
115 # sync displayed state with current history item
117 def refresh(opts = { })
119 index = @match.history.current
121 (@current + 1..index).each do |i|
122 animate(:forward, @match.history[i].state, @match.history[i].move, opts)
124 elsif index < @current
125 @current.downto(index + 1).each do |i|
126 animate(:back, @match.history[i - 1].state, @match.history[i].move, opts)
130 @board.highlight(@match.history[@current].move)
135 @match.history.go_to(index)
136 rescue History::OutOfBound
137 puts "error: no such index #{index}"
140 def animate(direction, state, move, opts = {})
141 anim = @animator.send(direction, state, move, opts)
148 @pools.each do |col, pool|
149 anim = pool.animator.warp(@match.history.state.pool(col))
154 def on_board_drop(data)
158 if data[:src] == data[:dst]
159 @board.selection = data[:src]
162 move = @policy.new_move(
163 @match.history.state, data[:src], data[:dst])
164 validate = @match.game.validator.new(@match.history.state)
168 if move and move.valid?
169 @board.add_to_group data[:item]
170 @board.lower data[:item]
171 perform! move, :adjust => true
175 elsif data[:index] and data[:dst]
177 move = @policy.new_move(
178 @match.history.state, nil, data[:dst],
179 :dropped => data[:item].name)
180 validate = @match.game.validator.new(@match.history.state)
182 @board.add_to_group data[:item]
183 @board.lower data[:item]
184 perform! move, :adjust => true, :dropped => data[:item]
191 def on_board_drag(data)
192 if @policy.movable?(@match.history.state, data[:src]) and
194 @board.raise data[:item]
195 @board.remove_from_group data[:item]
196 @board.selection = nil
201 def on_pool_drag(c, data)
202 if @policy.droppable?(@match.history.state, c, data[:index]) and
203 droppable?(c, data[:index])
206 # replace item with a correctly sized one
207 item = @board.create_piece(data[:item].name)
209 @board.remove_from_group item
210 anim = @pools[c].animator.remove_piece(data[:index])
212 data[:size] = @board.unit
213 data[:pool_color] = c
221 def on_pool_drop(color, data)
225 def cancel_drop(data)
226 anim = if data[:index]
227 # remove dragged item
229 # make original item reappear in its place
230 @pools[data[:pool_color]].animator.insert_piece(
234 @board.add_to_group data[:item]
235 @board.lower data[:item]
236 @animator.movement(data[:item], nil, data[:src], Path::Linear)
239 @field.run(anim) if anim
243 time.each do |pl, seconds|
244 @clocks[pl].clock ||= Clock.new(seconds, 0, nil)
245 @clocks[pl].clock.set_time(seconds)
250 @clocks.each do |pl, clock|
256 def add_controlled_player(player)
257 @controlled[player.color] = player
261 @match.close if @match
266 @controlled = { @color => self }
274 def droppable?(color, index)
281 return false unless @controlled[@match.history.state.turn]
282 if @match.history.current < @match.index