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 }
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
54 @board = @table.elements[:board]
55 @pools = @table.elements[:pools]
56 @clocks = @table.elements[:clocks]
58 @animator = @match.game.animator.new(@board)
59 @board.reset(match.state.board)
62 @clocks.each do |col, clock|
66 @board.observe(:click) {|p| on_board_click(p) }
67 @board.observe(:drag) {|data| on_board_drag(data) }
68 @board.observe(:drop) {|data| on_board_drop(data) }
69 @pools.each do |col, pool|
70 pool.observe(:drag) {|data| on_pool_drag(col, data) }
71 pool.observe(:drop) {|data| on_pool_drop(col, data) }
73 @clocks.each do |col, clock|
74 clock.data = { :color => col,
75 :player => match.player(col).name }
77 @noncontrolled = @match.observe(:move) do |data|
78 unless @controlled[data[:player].color] == data[:player]
79 animate(:forward, data[:state], data[:move])
80 @board.highlight(data[:move])
81 @clocks[data[:old_state].turn].stop
82 @clocks[data[:state].turn].start
86 @clocks[@match.game.players.first].active = true
87 @table.flip(@color != @match.game.players.first)
89 if @match.history.move
90 @board.highlight(@match.history.move)
94 def perform!(move, opts = {})
95 col = @match.state.turn
96 if @controlled[col] and @match.move(@controlled[col], move)
97 animate(:forward, @match.state, move, opts)
98 @board.highlight(move)
101 @clocks[@match.state.turn].start
106 state, move = @match.history.back
107 animate(:back, state, move)
108 @board.highlight(@match.history.move)
109 rescue History::OutOfBound
110 puts "error: first move"
114 state, move = @match.history.forward
115 animate(:forward, state, move)
116 @board.highlight(move)
117 rescue History::OutOfBound
118 puts "error: last move"
122 cur = @match.history.current
123 state, move = @match.history.go_to(index)
125 (cur + 1..index).each do |i|
126 animate(:forward, @match.history[i].state, @match.history[i].move)
128 @board.highlight(move)
130 (cur).downto(index + 1).each do |i|
131 animate(:back, @match.history[i - 1].state, @match.history[i].move)
133 @board.highlight(@match.history.move)
135 rescue History::OutOfBound
136 puts "error: no such index #{index}"
139 def animate(direction, state, move, opts = {})
140 anim = @animator.send(direction, state, move, opts)
147 @pools.each do |col, pool|
148 anim = pool.animator.warp(@match.state.pool(col))
153 def on_board_drop(data)
157 if data[:src] == data[:dst]
158 @board.selection = data[:src]
161 move = @policy.new_move(
162 @match.state, data[:src], data[:dst])
163 validate = @match.game.validator.new(@match.state)
167 if move and move.valid?
168 @board.add_to_group data[:item]
169 @board.lower data[:item]
170 perform! move, :adjust => true
174 elsif data[:index] and data[:dst]
176 move = @policy.new_move(
177 @match.state, nil, data[:dst],
178 :dropped => data[:item].name)
179 validate = @match.game.validator.new(@match.state)
181 @board.add_to_group data[:item]
182 @board.lower data[:item]
183 perform! move, :dropped => data[:item]
190 def on_board_drag(data)
191 if @policy.movable?(@match.state, data[:src]) and
193 @board.raise data[:item]
194 @board.remove_from_group data[:item]
195 @board.selection = nil
200 def on_pool_drag(c, data)
201 if @policy.droppable?(@match.state, c, data[:index]) and
202 droppable?(c, data[:index])
205 # replace item with a correctly sized one
206 item = @board.create_piece(data[:item].name)
208 @board.remove_from_group item
209 anim = @pools[c].animator.remove_piece(data[:index])
211 data[:size] = @board.unit
212 data[:pool_color] = c
220 def on_pool_drop(color, data)
224 def cancel_drop(data)
225 anim = if data[:index]
226 # remove dragged item
228 # make original item reappear in its place
229 @pools[data[:pool_color]].animator.insert_piece(
233 @board.add_to_group data[:item]
234 @board.lower data[:item]
235 @animator.movement(data[:item], nil, data[:src], Path::Linear)
238 @field.run(anim) if anim
242 time.each do |pl, seconds|
243 @clocks[pl].clock ||= Clock.new(seconds, 0, nil)
244 @clocks[pl].clock.set_time(seconds)
249 @clocks.each do |pl, clock|
255 def add_controlled_player(player)
256 @controlled[player.color] = player
261 # ignore further moves from other players
262 @match.delete_observer(@noncontrolled)
267 @controlled = { @color => self }
275 def droppable?(color, index)
282 return false unless @controlled[@match.state.turn]
283 if @match.history.current < @match.index