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)
87 def perform!(move, opts = {})
88 col = @match.state.turn
89 if @controlled[col] and @match.move(@controlled[col], move)
90 animate(:forward, @match.state, move, opts)
91 @board.highlight(move)
94 @clocks[@match.state.turn].start
99 state, move = @match.history.back
100 animate(:back, state, move)
101 @board.highlight(@match.history.move)
102 rescue History::OutOfBound
103 puts "error: first move"
107 state, move = @match.history.forward
108 animate(:forward, state, move)
109 @board.highlight(move)
110 rescue History::OutOfBound
111 puts "error: last move"
115 cur = @match.history.current
116 state, move = @match.history.go_to(index)
118 (cur + 1..index).each do |i|
119 animate(:forward, @match.history[i].state, @match.history[i].move)
121 @board.highlight(move)
123 (cur).downto(index + 1).each do |i|
124 animate(:back, @match.history[i - 1].state, @match.history[i].move)
126 @board.highlight(@match.history.move)
128 rescue History::OutOfBound
129 puts "error: no such index #{index}"
132 def animate(direction, state, move, opts = {})
133 anim = @animator.send(direction, state, move, opts)
140 @pools.each do |col, pool|
141 anim = pool.animator.warp(@match.state.pool(col))
146 def on_board_drop(data)
150 if data[:src] == data[:dst]
151 @board.selection = data[:src]
154 move = @match.game.policy.new_move(
155 @match.state, data[:src], data[:dst])
156 validate = @match.game.validator.new(@match.state)
160 if move and move.valid?
161 @board.add_to_group data[:item]
162 @board.lower data[:item]
163 perform! move, :adjust => true
167 elsif data[:index] and data[:dst]
169 move = @match.game.policy.new_move(
170 @match.state, nil, data[:dst],
171 :dropped => data[:item].name)
172 validate = @match.game.validator.new(@match.state)
174 @board.add_to_group data[:item]
175 @board.lower data[:item]
176 perform! move, :dropped => data[:item]
183 def on_board_drag(data)
184 if @match.game.policy.movable?(@match.state, data[:src]) and
186 @board.raise data[:item]
187 @board.remove_from_group data[:item]
188 @board.selection = nil
193 def on_pool_drag(c, data)
194 if @match.game.policy.droppable?(@match.state, c, data[:index]) and
195 droppable?(c, data[:index])
198 # replace item with a correctly sized one
199 item = @board.create_piece(data[:item].name)
201 @board.remove_from_group item
202 anim = @pools[c].animator.remove_piece(data[:index])
204 data[:size] = @board.unit
205 data[:pool_color] = c
213 def on_pool_drop(color, data)
217 def cancel_drop(data)
218 anim = if data[:index]
219 # remove dragged item
221 # make original item reappear in its place
222 @pools[data[:pool_color]].animator.insert_piece(
226 @board.add_to_group data[:item]
227 @board.lower data[:item]
228 @animator.movement(data[:item], nil, data[:src], Path::Linear)
231 @field.run(anim) if anim
235 time.each do |pl, seconds|
236 @clocks[pl].clock ||= Clock.new(seconds, 0, nil)
237 @clocks[pl].clock.set_time(seconds)
241 def add_controlled_player(player)
242 @controlled[player.color] = player
247 @controlled = { @color => self }
254 def droppable?(color, index)
261 return false unless @controlled[@match.state.turn]
262 if @match.history.current < @match.index