Use normal binding for SimpleObserver.
[kaya.git] / lib / controller.rb
blobadb7d6e23af0ecc519ab716a54f71798d777c29a
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     @board.observe(:click) {|p| on_board_click(p) }
61     @board.observe(:drag) {|data| on_board_drag(data) }
62     @board.observe(:drop) {|data| on_board_drop(data) }
63     @pools.each do |col, pool|
64       pool.observe(:drag) {|data| on_pool_drag(col, data) }
65       pool.observe(:drop) {|data| on_pool_drop(col, data) }
66     end
67     @clocks.each do |col, clock|
68       clock.clock = Clock.new(300, 0, nil)
69       clock.data = { :color => col }
70     end
71     
72     @clocks[@match.game.players.first].active = true
73   end
74   
75   def perform!(move, opts = {})
76     col = @match.state.turn
77     if @controlled[col] and @match.move(@controlled[col], move)
78       animate(:forward, @match.state, move, opts)
79       @board.highlight(move)
80       
81       @clocks[col].stop
82       @clocks[@match.state.turn].start
83     end
84   end
85   
86 #   def back
87 #     state, move = @history.back
88 #     animate(:back, state, move)
89 #     @board.highlight(@history.move)
90 #   rescue History::OutOfBound
91 #     puts "error: first move"
92 #   end
93 #   
94 #   def forward
95 #     state, move = @history.forward
96 #     animate(:forward, state, move)
97 #     @board.highlight(move)
98 #   rescue History::OutOfBound
99 #     puts "error: last move"
100 #   end
101   
102   def animate(direction, state, move, opts = {})
103     anim = @animator.send(direction, state, move, opts)
104     @field.run anim
105     
106     update_pools
107   end
108   
109   def update_pools
110     @pools.each do |col, pool|
111       anim = pool.animator.warp(@match.state.pool(col))
112       @field.run anim
113     end
114   end
115   
116   def on_board_drop(data)
117     if data[:src]
118       move = nil
119       
120       if data[:src] == data[:dst]
121         @board.selection = data[:src]
122       elsif data[:dst]
123         # normal move
124         move = @match.game.policy.new_move(
125           @match.state, data[:src], data[:dst])
126         validate = @match.game.validator.new(@match.state)
127         validate[move]
128       end
129       
130       if move and move.valid?
131         @board.add_to_group data[:item]
132         @board.lower data[:item]
133         perform! move, :adjust => true
134       else
135         cancel_drop(data)
136       end
137     elsif data[:index] and data[:dst]
138       # actual drop
139       move = @match.game.policy.new_move(
140         @match.state, nil, data[:dst], 
141         :dropped => data[:item].name)
142       validate = @match.game.validator.new(@match.state)
143       if validate[move]
144         @board.add_to_group data[:item]
145         @board.lower data[:item]
146         perform! move, :dropped => data[:item]
147       else
148         cancel_drop(data)
149       end
150     end
151   end
152   
153   def on_board_drag(data)
154     if @match.game.policy.movable?(@match.state, data[:src]) and 
155        movable?(data[:src])
156       @board.raise data[:item]
157       @board.remove_from_group data[:item]
158       @board.selection = nil
159       @scene.on_drag(data)
160     end
161   end
162   
163   def on_pool_drag(c, data)
164     if @match.game.policy.droppable?(@match.state, c, data[:index]) and 
165        droppable?(c, data[:index])
166        
167        
168       # replace item with a correctly sized one
169       item = @board.create_piece(data[:item].name)
170       @board.raise item
171       @board.remove_from_group item
172       anim = @pools[c].animator.remove_piece(data[:index])
173       data[:item] = item
174       data[:size] = @board.unit
175       data[:pool_color] = c
176       
177       @scene.on_drag(data)
178       
179       @field.run anim
180     end
181   end
182   
183   def on_pool_drop(color, data)
184     cancel_drop(data)
185   end
186   
187   def cancel_drop(data)
188     anim = if data[:index]
189       # remove dragged item
190       data[:item].remove
191       # make original item reappear in its place
192       @pools[data[:pool_color]].animator.insert_piece(
193         data[:index],
194         data[:item].name)
195     elsif data[:src]
196       @board.add_to_group data[:item]
197       @board.lower data[:item]
198       @animator.movement(data[:item], nil, data[:src], Path::Linear)
199     end
200     
201     @field.run(anim) if anim
202   end
203   
204   def add_controlled_player(player)
205     @controlled[player.color] = player
206   end
207   
208   def color=(value)
209     @color = value
210     @controlled = { @color => self }
211   end
212     
213   def movable?(p)
214     ! ! @controlled[@match.state.turn]
215   end
216   
217   def droppable?(color, index)
218     ! ! @controlled[@match.state.turn]
219   end