Refactoring: use Match to handle interactions.
[kaya.git] / lib / controller.rb
blob0fcb209c0d3245bde2159cd1472e91056a51c9e2
1 require 'observer_utils'
2 require 'history'
3 require 'board/pool_animator'
4 require 'clock'
5 require 'interaction/match'
7 class Controller
8   include Observer
9   
10   attr_reader :history
11   attr_reader :color
12   attr_reader :controlled
13   
14   def initialize(table)
15     @table = table
16     @scene = @table.scene
18     @pools = { }
19     @clocks = { }
20     
21     @field = AnimationField.new(20)
22   end
23   
24   def each_element
25     yield @board if @board
26     @pools.each {|c, pool| yield pool }
27     @clocks.each {|c, clock| yield clock }
28   end
29   
30   def on_board_click(p)
31     state = @match.state
32     if @board.selection
33       move = @match.game.policy.new_move(state, @board.selection, p)
34       validate = @match.game.validator.new(state)
35       if validate[move]
36         perform! move
37       end
38       
39       @board.selection = nil
40     elsif @match.game.policy.movable?(state, p) and movable?(p)
41       @board.selection = p
42     end
43   end
44   
45   def reset(match)
46     @match = match
47     @table.reset(match.game)
48     @board = @table.elements[:board]
49     @pools = @table.elements[:pools]
50     @clocks = @table.elements[:clocks]
51     
52     @animator = @match.game.animator.new(@board)
53     @board.reset(match.state.board)
54     update_pools
55     
56     @clocks.each do |col, clock|
57       clock.stop
58     end
59     
60     c = self
61     @board.observe(:click) {|p| c.on_board_click(p) }
62     @board.observe(:drag) {|data| c.on_board_drag(data) }
63     @board.observe(:drop) {|data| c.on_board_drop(data) }
64     @pools.each do |col, pool|
65       pool.observe(:drag) {|data| c.on_pool_drag(col, data) }
66       pool.observe(:drop) {|data| c.on_pool_drop(col, data) }
67     end
68     @clocks.each do |col, clock|
69       clock.clock = Clock.new(300, 0, nil)
70       clock.data = { :color => col }
71     end
72     
73     @clocks[@match.game.players.first].active = true
74   end
75   
76   def perform!(move, opts = {})
77     col = @match.state.turn
78     if @controlled[col] and @match.move(@controlled[col], move)
79       animate(:forward, @match.state, move, opts)
80       @board.highlight(move)
81       
82       @clocks[col].stop
83       @clocks[@match.state.turn].start
84     end
85   end
86   
87 #   def back
88 #     state, move = @history.back
89 #     animate(:back, state, move)
90 #     @board.highlight(@history.move)
91 #   rescue History::OutOfBound
92 #     puts "error: first move"
93 #   end
94 #   
95 #   def forward
96 #     state, move = @history.forward
97 #     animate(:forward, state, move)
98 #     @board.highlight(move)
99 #   rescue History::OutOfBound
100 #     puts "error: last move"
101 #   end
102   
103   def animate(direction, state, move, opts = {})
104     anim = @animator.send(direction, state, move, opts)
105     @field.run anim
106     
107     update_pools
108   end
109   
110   def update_pools
111     @pools.each do |col, pool|
112       anim = pool.animator.warp(@match.state.pool(col))
113       @field.run anim
114     end
115   end
116   
117   def on_board_drop(data)
118     if data[:src]
119       move = nil
120       
121       if data[:src] == data[:dst]
122         @board.selection = data[:src]
123       elsif data[:dst]
124         # normal move
125         move = @match.game.policy.new_move(
126           @match.state, data[:src], data[:dst])
127         validate = @match.game.validator.new(@match.state)
128         validate[move]
129       end
130       
131       if move and move.valid?
132         @board.add_to_group data[:item]
133         @board.lower data[:item]
134         perform! move, :adjust => true
135       else
136         cancel_drop(data)
137       end
138     elsif data[:index] and data[:dst]
139       # actual drop
140       move = @match.game.policy.new_move(
141         @match.state, nil, data[:dst], 
142         :dropped => data[:item].name)
143       validate = @match.game.validator.new(@match.state)
144       if validate[move]
145         @board.add_to_group data[:item]
146         @board.lower data[:item]
147         perform! move, :dropped => data[:item]
148       else
149         cancel_drop(data)
150       end
151     end
152   end
153   
154   def on_board_drag(data)
155     if @match.game.policy.movable?(@match.state, data[:src]) and 
156        movable?(data[:src])
157       @board.raise data[:item]
158       @board.remove_from_group data[:item]
159       @board.selection = nil
160       @scene.on_drag(data)
161     end
162   end
163   
164   def on_pool_drag(c, data)
165     if @match.game.policy.droppable?(@match.state, c, data[:index]) and 
166        droppable?(c, data[:index])
167        
168        
169       # replace item with a correctly sized one
170       item = @board.create_piece(data[:item].name)
171       @board.raise item
172       @board.remove_from_group item
173       anim = @pools[c].animator.remove_piece(data[:index])
174       data[:item] = item
175       data[:size] = @board.unit
176       data[:pool_color] = c
177       
178       @scene.on_drag(data)
179       
180       @field.run anim
181     end
182   end
183   
184   def on_pool_drop(color, data)
185     cancel_drop(data)
186   end
187   
188   def cancel_drop(data)
189     anim = if data[:index]
190       # remove dragged item
191       data[:item].remove
192       # make original item reappear in its place
193       @pools[data[:pool_color]].animator.insert_piece(
194         data[:index],
195         data[:item].name)
196     elsif data[:src]
197       @board.add_to_group data[:item]
198       @board.lower data[:item]
199       @animator.movement(data[:item], nil, data[:src], Path::Linear)
200     end
201     
202     @field.run(anim) if anim
203   end
204   
205   def add_controlled_player(player)
206     @controlled[player.color] = player
207   end
208   
209   def color=(value)
210     @color = value
211     @controlled = { @color => self }
212   end
213     
214   def movable?(p)
215     ! ! @controlled[@match.state.turn]
216   end
217   
218   def droppable?(color, index)
219     ! ! @controlled[@match.state.turn]
220   end