Implemented drag and drop.
[kaya.git] / lib / controller.rb
blob7428455d9314ce342ccfccae474ea0cc323818d6
1 require 'observer_utils'
2 require 'history'
3 require 'board/pool_animator'
5 class Controller
6   include Observer
7   
8   attr_reader :history
9   
10   def initialize(scene, elements, game, history)
11     @scene = scene
12     @board = elements[:board]
13     @pools = elements[:pools]
15     @game = game
16     @history = history
17     @animator = @game.animator.new(@board)
18     @field = AnimationField.new(20)
19     @board.reset(history.state.board)
20     
21     c = self
22     @board.observe(:click) {|p| c.on_board_click(p) }
23     @board.observe(:drag) {|data| c.on_board_drag(data) }
24     @board.observe(:drop) {|data| c.on_board_drop(data) }
25     @pools.each do |color, pool|
26       pool.observe(:drag) {|data| c.on_pool_drag(color, data) }
27       pool.observe(:drop) {|data| c.on_pool_drop(color, data) }
28     end
29   end
30   
31   def on_board_click(p)
32     state = @history.state
33     if @board.selection
34       move = @game.policy.new_move(state, @board.selection, p)
35       validate = @game.validator.new(state)
36       if validate[move]
37         perform! move
38       end
39       
40       @board.selection = nil
41     elsif @game.policy.movable?(state, p) and movable?(p)
42       @board.selection = p
43     end
44   end
45   
46   def perform!(move, opts = {})
47     state = @history.state.dup
48     state.perform! move
49     @history.add_move(state, move)
50     animate(:forward, state, move, opts)
51     @board.highlight(move)
52   end
53   
54   def back
55     state, move = @history.back
56     animate(:back, state, move)
57     @board.highlight(@history.move)
58   rescue History::OutOfBound
59     puts "error: first move"
60   end
61   
62   def forward
63     state, move = @history.forward
64     animate(:forward, state, move)
65     @board.highlight(move)
66   rescue History::OutOfBound
67     puts "error: last move"
68   end
69   
70   def animate(direction, state, move, opts = {})
71     anim = @animator.send(direction, state, move, opts)
72     @field.run anim
73     
74     update_pools
75   end
76   
77   def update_pools
78     @pools.each do |color, pool|
79       anim = pool.animator.warp(@history.state.pool(color))
80       @field.run anim
81     end
82   end
83   
84   def on_board_drop(data)
85     if data[:src]
86       move = nil
87       
88       if data[:src] == data[:dst]
89         @board.selection = data[:src]
90       elsif data[:dst]
91         # normal move
92         move = @game.policy.new_move(@history.state, data[:src], data[:dst])
93         validate = @game.validator.new(@history.state)
94         validate[move]
95       end
96       
97       if move and move.valid?
98         @board.add_to_group data[:item]
99         @board.lower data[:item]
100         perform! move, :adjust => true
101       else
102         cancel_drop(data)
103       end
104     elsif data[:index] and data[:dst]
105       # actual drop
106       move = @game.policy.new_move(@history.state, nil,
107         data[:dst], :dropped => data[:item].name)
108       validate = @game.validator.new(@history.state)
109       if validate[move]
110         @board.add_to_group data[:item]
111         @board.lower data[:item]
112         perform! move, :dropped => data[:item]
113       else
114         cancel_drop(data)
115       end
116     end
117   end
118   
119   def on_board_drag(data)
120     if @game.policy.movable?(@history.state, data[:src]) and 
121        movable?(data[:src])
122       @board.raise data[:item]
123       @board.remove_from_group data[:item]
124       @board.selection = nil
125       @scene.on_drag(data)
126     end
127   end
128   
129   def on_pool_drag(color, data)
130     if @game.policy.droppable?(@history.state, color, data[:index]) and 
131        droppable?(color, data[:index])
132        
133       # replace item with a correctly sized one
134       item = @board.create_piece(data[:item].name)
135       @board.raise item
136       @board.remove_from_group item
137       anim = @pools[color].animator.remove_piece(data[:index])
138       data[:item] = item
139       data[:size] = @board.unit
140       data[:pool_color] = color
141       
142       @scene.on_drag(data)
143       
144       @field.run anim
145     end
146   end
147   
148   def on_pool_drop(color, data)
149     cancel_drop(data)
150   end
151   
152   def cancel_drop(data)
153     anim = if data[:index]
154       # remove dragged item
155       data[:item].remove
156       # make original item reappear in its place
157       @pools[data[:pool_color]].animator.insert_piece(
158         data[:index],
159         data[:item].name)
160     elsif data[:src]
161       @board.add_to_group data[:item]
162       @board.lower data[:item]
163       @animator.movement(data[:item], nil, data[:src], Path::Linear)
164     end
165     
166     @field.run(anim) if anim
167   end
168   
169   def movable?(p)
170     true
171   end
172   
173   def droppable?(color, index)
174     true
175   end