comparison gamelib/gameboard.py @ 472:67574723427e

Partially working (but much less crashy) multiple select support
author Neil Muller <drnlmuller@gmail.com>
date Wed, 25 Nov 2009 14:32:40 +0000
parents fbbd39cfc975
children 4b8ca482ea08
comparison
equal deleted inserted replaced
471:c34d53edfa12 472:67574723427e
100 # Ensure we don't every try to create more foxes then is sane 100 # Ensure we don't every try to create more foxes then is sane
101 self.max_foxes = level.max_foxes 101 self.max_foxes = level.max_foxes
102 self.calculate_wood_groat_exchange_rate() 102 self.calculate_wood_groat_exchange_rate()
103 103
104 self.selected_tool = None 104 self.selected_tool = None
105 self.animal_to_place = None
106 self.sprite_cursor = None 105 self.sprite_cursor = None
107 self.chickens = set() 106 self.chickens = set()
108 self.foxes = set() 107 self.foxes = set()
109 self.buildings = set() 108 self.buildings = set()
110 self._pos_cache = AnimalPositionCache(self) 109 self._pos_cache = AnimalPositionCache(self)
127 for tn in equipment.EQUIP_MAP: 126 for tn in equipment.EQUIP_MAP:
128 cdata[tn] = (self.add_start_chickens, tn) 127 cdata[tn] = (self.add_start_chickens, tn)
129 128
130 self.tv.run_codes(cdata, (0,0,width,height)) 129 self.tv.run_codes(cdata, (0,0,width,height))
131 130
131 self.selected_chickens = []
132
132 def get_top_widget(self): 133 def get_top_widget(self):
133 return self.top_widget 134 return self.top_widget
134 135
135 def create_display(self): 136 def create_display(self):
136 width, height = self.disp.rect.w, self.disp.rect.h 137 width, height = self.disp.rect.w, self.disp.rect.h
173 def loop(self): 174 def loop(self):
174 self.tv.loop() 175 self.tv.loop()
175 176
176 def set_selected_tool(self, tool, cursor): 177 def set_selected_tool(self, tool, cursor):
177 if not self.day: 178 if not self.day:
178 return 179 return False
180 if self.apply_tool_to_selected(tool):
181 return False # Using the tool on selected chickens is immediate
179 self.selected_tool = tool 182 self.selected_tool = tool
180 if self.animal_to_place: 183 if tool in [None, constants.TOOL_SELL_BUILDING,
181 # Clear any highlights 184 constants.TOOL_REPAIR_BUILDING]:
182 self.animal_to_place.unequip_by_name("Spotlight") 185 # FIXME - this special casing is sucky
183 self.select_animal_to_place(None) 186 self.unselect_all()
184 sprite_curs = None 187 sprite_curs = None
185 if buildings.is_building(tool): 188 if buildings.is_building(tool):
186 sprite_curs = sprite_cursor.SpriteCursor(tool.IMAGE, self.tv, tool.BUY_PRICE) 189 sprite_curs = sprite_cursor.SpriteCursor(tool.IMAGE, self.tv, tool.BUY_PRICE)
187 elif equipment.is_equipment(tool): 190 elif equipment.is_equipment(tool):
188 sprite_curs = sprite_cursor.SpriteCursor(tool.CHICKEN_IMAGE_FILE, self.tv) 191 sprite_curs = sprite_cursor.SpriteCursor(tool.CHICKEN_IMAGE_FILE, self.tv)
189 self.set_cursor(cursor, sprite_curs) 192 self.set_cursor(cursor, sprite_curs)
193 return True
194
195 def apply_tool_to_selected(self, tool):
196 if self.selected_chickens:
197 # dispatch call to selected chickens if appropriate
198 if tool == constants.TOOL_SELL_CHICKEN:
199 self.sell_chicken(None)
200 return True
201 elif tool == constants.TOOL_SELL_EGG:
202 self.sell_egg(None)
203 return True
204 elif tool == constants.TOOL_SELL_EQUIPMENT:
205 self.sell_equipment(None)
206 return True
207 elif equipment.is_equipment(tool):
208 self.buy_equipment(None, tool)
209 return True
210 return False
190 211
191 def set_cursor(self, cursor=None, sprite_curs=None): 212 def set_cursor(self, cursor=None, sprite_curs=None):
192 if cursor: 213 if cursor:
193 pygame.mouse.set_cursor(*cursor) 214 pygame.mouse.set_cursor(*cursor)
194 else: 215 else:
253 self.sell_chicken(self.tv.screen_to_tile(e.pos)) 274 self.sell_chicken(self.tv.screen_to_tile(e.pos))
254 elif self.selected_tool == constants.TOOL_SELL_EGG: 275 elif self.selected_tool == constants.TOOL_SELL_EGG:
255 self.sell_egg(self.tv.screen_to_tile(e.pos)) 276 self.sell_egg(self.tv.screen_to_tile(e.pos))
256 elif self.selected_tool == constants.TOOL_PLACE_ANIMALS: 277 elif self.selected_tool == constants.TOOL_PLACE_ANIMALS:
257 self.place_animal(self.tv.screen_to_tile(e.pos)) 278 self.place_animal(self.tv.screen_to_tile(e.pos))
279 elif self.selected_tool == constants.TOOL_SELECT_CHICKENS:
280 self.select_chicken(self.tv.screen_to_tile(e.pos))
258 elif self.selected_tool == constants.TOOL_SELL_BUILDING: 281 elif self.selected_tool == constants.TOOL_SELL_BUILDING:
259 self.sell_building(self.tv.screen_to_tile(e.pos)) 282 self.sell_building(self.tv.screen_to_tile(e.pos))
260 elif self.selected_tool == constants.TOOL_SELL_EQUIPMENT: 283 elif self.selected_tool == constants.TOOL_SELL_EQUIPMENT:
261 self.sell_equipment(self.tv.screen_to_tile(e.pos)) 284 self.sell_equipment(self.tv.screen_to_tile(e.pos))
262 elif self.selected_tool == constants.TOOL_REPAIR_BUILDING: 285 elif self.selected_tool == constants.TOOL_REPAIR_BUILDING:
263 self.repair_building(self.tv.screen_to_tile(e.pos)) 286 self.repair_building(self.tv.screen_to_tile(e.pos))
264 elif buildings.is_building(self.selected_tool): 287 elif buildings.is_building(self.selected_tool):
265 self.buy_building(self.tv.screen_to_tile(e.pos), self.selected_tool) 288 self.buy_building(self.tv.screen_to_tile(e.pos), self.selected_tool)
266 elif equipment.is_equipment(self.selected_tool): 289 elif equipment.is_equipment(self.selected_tool):
267 self.buy_equipment(self.tv.screen_to_tile(e.pos), self.selected_tool) 290 if not self.selected_chickens:
291 # old selection behaviour
292 self.buy_equipment(self.tv.screen_to_tile(e.pos),
293 self.selected_tool)
268 294
269 def get_outside_chicken(self, tile_pos): 295 def get_outside_chicken(self, tile_pos):
270 for chick in self.chickens: 296 for chick in self.chickens:
271 if chick.covers(tile_pos) and chick.outside(): 297 if chick.covers(tile_pos) and chick.outside():
272 return chick 298 return chick
295 if update_button: 321 if update_button:
296 update_button(chicken, empty=True) 322 update_button(chicken, empty=True)
297 self.remove_chicken(chicken) 323 self.remove_chicken(chicken)
298 return True 324 return True
299 325
300 chick = self.get_outside_chicken(tile_pos) 326 if tile_pos:
301 if chick is None: 327 chick = self.get_outside_chicken(tile_pos)
302 building = self.get_building(tile_pos) 328 if chick is None:
303 if building and building.HENHOUSE: 329 building = self.get_building(tile_pos)
304 self.open_building_dialog(building, do_sell) 330 if building and building.HENHOUSE:
305 return 331 self.open_building_dialog(building, do_sell)
306 do_sell(chick) 332 return
333 do_sell(chick)
334 else:
335 for chick in self.selected_chickens[:]:
336 do_sell(chick)
307 337
308 def sell_one_egg(self, chicken): 338 def sell_one_egg(self, chicken):
309 if chicken.eggs: 339 if chicken.eggs:
310 self.add_cash(self.level.sell_price_egg) 340 self.add_cash(self.level.sell_price_egg)
311 chicken.remove_one_egg() 341 chicken.remove_one_egg()
327 357
328 building = self.get_building(tile_pos) 358 building = self.get_building(tile_pos)
329 if building and building.HENHOUSE: 359 if building and building.HENHOUSE:
330 self.open_building_dialog(building, do_sell) 360 self.open_building_dialog(building, do_sell)
331 361
332 def select_animal_to_place(self, animal): 362 def select_animal(self, animal, extend=True):
333 if self.animal_to_place: 363 if extend:
334 self.animal_to_place.unequip_by_name("Spotlight") 364 self.selected_chickens.append(animal)
335 self.animal_to_place = animal 365 animal.equip(equipment.Spotlight())
336 if self.animal_to_place: 366 else:
337 self.animal_to_place.equip(equipment.Spotlight()) 367 self.unselect_all()
368 self.selected_chickens.append(animal)
369 animal.equip(equipment.Spotlight())
370
371 def unselect_all(self):
372 # Clear any highlights
373 for chick in self.selected_chickens:
374 chick.unequip_by_name("Spotlight")
375 self.selected_chickens = []
376
377 def unselect_animal(self, animal):
378 if animal in self.selected_chickens:
379 self.selected_chickens.remove(animal)
380 animal.unequip_by_name("Spotlight")
381
382 def get_chicken_at_pos(self, tile_pos):
383 chicken = self.get_outside_chicken(tile_pos)
384 if chicken:
385 return chicken
386 building = self.get_building(tile_pos)
387 if building:
388 self.open_building_dialog(building)
389
390 def select_chicken(self, tile_pos):
391 """Handle a select chicken event"""
392 # Get the chicken at this position
393 chicken = self.get_chicken_at_pos(tile_pos)
394 if not chicken:
395 return # do nothing
396 elif chicken in self.selected_chickens:
397 self.unselect_animal(chicken)
398 else:
399 self.select_animal(chicken)
338 400
339 def place_animal(self, tile_pos): 401 def place_animal(self, tile_pos):
340 """Handle an TOOL_PLACE_ANIMALS click. 402 """Handle an TOOL_PLACE_ANIMALS click.
341 403
342 This will either select an animal or 404 This will either select an animal or
343 place a selected animal in a building. 405 place a selected animal in a building.
344 """ 406 """
345 chicken = self.get_outside_chicken(tile_pos) 407 if tile_pos and not self.selected_chickens:
346 if chicken: 408 # Old behaviour
347 if chicken is self.animal_to_place: 409 chicken = self.get_chicken_at_pos(tile_pos)
348 self.select_animal_to_place(None) 410 if chicken:
349 pygame.mouse.set_cursor(*cursors.cursors['select']) 411 self.select_animal(chicken)
350 else:
351 self.select_animal_to_place(chicken)
352 pygame.mouse.set_cursor(*cursors.cursors['chicken']) 412 pygame.mouse.set_cursor(*cursors.cursors['chicken'])
353 return 413 return
354 building = self.get_building(tile_pos) 414 elif tile_pos:
355 if building and building.ABODE: 415 building = self.get_building(tile_pos)
356 if self.animal_to_place: 416 if building and building.ABODE:
357 try: 417 for chicken in self.selected_chickens:
358 place = building.first_empty_place() 418 try:
359 self.relocate_animal(self.animal_to_place, place=place) 419 place = building.first_empty_place()
360 self.animal_to_place.equip(equipment.Nest()) 420 self.relocate_animal(chicken, place=place)
361 self.select_animal_to_place(None) 421 chicken.equip(equipment.Nest())
362 pygame.mouse.set_cursor(*cursors.cursors['select']) 422 pygame.mouse.set_cursor(*cursors.cursors['select'])
363 except buildings.BuildingFullError: 423 except buildings.BuildingFullError:
364 pass 424 pass
365 else: 425 else:
366 self.open_building_dialog(building) 426 self.open_building_dialog(building)
367 return 427 return
368 if self.tv.get(tile_pos) == self.GRASSLAND: 428 if self.tv.get(tile_pos) == self.GRASSLAND:
369 if self.animal_to_place is not None: 429 for chicken in self.selected_chickens:
370 self.animal_to_place.unequip_by_name("Nest") 430 # FIXME: find free square nearby
371 self.relocate_animal(self.animal_to_place, tile_pos=tile_pos) 431 if not self.get_outside_chicken(tile_pos):
372 self.eggs -= self.animal_to_place.get_num_eggs() 432 chicken.unequip_by_name("Nest")
373 self.animal_to_place.remove_eggs() 433 self.relocate_animal(chicken, tile_pos=tile_pos)
374 self.toolbar.update_egg_counter(self.eggs) 434 self.eggs -= chicken.get_num_eggs()
435 chicken.remove_eggs()
436 self.toolbar.update_egg_counter(self.eggs)
375 437
376 def relocate_animal(self, chicken, tile_pos=None, place=None): 438 def relocate_animal(self, chicken, tile_pos=None, place=None):
377 assert((tile_pos, place) != (None, None)) 439 assert((tile_pos, place) != (None, None))
378 if chicken.abode is not None: 440 if chicken.abode is not None:
379 chicken.abode.clear_occupant() 441 chicken.abode.clear_occupant()
437 def nest_clicked(place, button): 499 def nest_clicked(place, button):
438 """Handle a nest being clicked.""" 500 """Handle a nest being clicked."""
439 if place.occupant: 501 if place.occupant:
440 # there is an occupant, select or sell it 502 # there is an occupant, select or sell it
441 if not sell_callback: 503 if not sell_callback:
442 old_animal = self.animal_to_place 504 self.select_animal(place.occupant)
443 self.select_animal_to_place(place.occupant)
444 # deselect old animal (on button)
445 update_button(old_animal)
446 # select new animal (on button) 505 # select new animal (on button)
447 update_button(self.animal_to_place) 506 update_button(place.occupant)
448 else: 507 else:
449 # Attempt to sell the occupant 508 # Attempt to sell the occupant
450 sell_callback(place.occupant, update_button) 509 sell_callback(place.occupant, update_button)
451 else: 510 else:
452 # there is no occupant, attempt to fill the space 511 # there is no occupant, attempt to fill the space
453 if self.animal_to_place is not None: 512 if self.selected_chickens:
454 # empty old nest (on button) 513 # empty old nest (on button)
455 update_button(self.animal_to_place, empty=True) 514 update_button(self.selected_chickens[0], empty=True)
456 self.relocate_animal(self.animal_to_place, place=place) 515 self.relocate_animal(self.selected_chickens[0], place=place)
457 # populate the new nest (on button) 516 # populate the new nest (on button)
458 update_button(self.animal_to_place) 517 update_button(self.selected_chickens[0])
459 518
460 tbl = gui.Table() 519 tbl = gui.Table()
461 columns = building.max_floor_width() 520 columns = building.max_floor_width()
462 kwargs = { 'style': { 'padding_left': 10, 'padding_bottom': 10 }} 521 kwargs = { 'style': { 'padding_left': 10, 'padding_bottom': 10 }}
463 for floor in building.floors(): 522 for floor in building.floors():
478 building.selected(True) 537 building.selected(True)
479 def close_callback(): 538 def close_callback():
480 building.selected(False) 539 building.selected(False)
481 540
482 def evict_callback(): 541 def evict_callback():
483 if not self.animal_to_place: 542 if not self.selected_chickens:
484 return 543 return
485 for tile_pos in building.adjacent_tiles(): 544 for chicken in self.selected_chickens:
486 if self.tv.get(tile_pos) != self.GRASSLAND: 545 for tile_pos in building.adjacent_tiles():
487 continue 546 if self.tv.get(tile_pos) != self.GRASSLAND:
488 if self.get_outside_chicken(tile_pos) is None: 547 continue
489 update_button(self.animal_to_place, empty=True) 548 if self.get_outside_chicken(tile_pos) is None:
490 self.place_animal(tile_pos) 549 update_button(chicken, empty=True)
491 break 550 self.place_animal(tile_pos)
551 break
492 552
493 if not sell_callback: 553 if not sell_callback:
494 tbl.tr() 554 tbl.tr()
495 button = gui.Button('Evict') 555 button = gui.Button('Evict')
496 button.connect(gui.CLICK, evict_callback) 556 button.connect(gui.CLICK, evict_callback)
521 chicken.equip(equipment) 581 chicken.equip(equipment)
522 if update_button: 582 if update_button:
523 update_button(chicken) 583 update_button(chicken)
524 return False 584 return False
525 585
526 chicken = self.get_outside_chicken(tile_pos) 586 if tile_pos:
527 if chicken is None: 587 chicken = self.get_outside_chicken(tile_pos)
528 building = self.get_building(tile_pos) 588 if chicken is None:
529 if not (building and building.ABODE): 589 building = self.get_building(tile_pos)
530 return 590 if not (building and building.ABODE):
531 # Bounce through open dialog once more 591 return
532 self.open_building_dialog(building, do_equip) 592 # Bounce through open dialog once more
593 self.open_building_dialog(building, do_equip)
594 else:
595 do_equip(chicken)
533 else: 596 else:
534 do_equip(chicken) 597 for chicken in self.selected_chickens:
598 do_equip(chicken)
535 599
536 def sell_building(self, tile_pos): 600 def sell_building(self, tile_pos):
537 building = self.get_building(tile_pos) 601 building = self.get_building(tile_pos)
538 if building is None: 602 if building is None:
539 return 603 return
740 fox.building.remove_predator(fox) 804 fox.building.remove_predator(fox)
741 if fox in self.tv.sprites: 805 if fox in self.tv.sprites:
742 self.tv.sprites.remove(fox) 806 self.tv.sprites.remove(fox)
743 807
744 def remove_chicken(self, chick): 808 def remove_chicken(self, chick):
745 if chick is self.animal_to_place: 809 if chick in self.selected_chickens:
746 self.select_animal_to_place(None) 810 self.unselect_animal(chick)
747 self.chickens.discard(chick) 811 self.chickens.discard(chick)
748 self.eggs -= chick.get_num_eggs() 812 self.eggs -= chick.get_num_eggs()
749 self.toolbar.update_egg_counter(self.eggs) 813 self.toolbar.update_egg_counter(self.eggs)
750 if chick.abode: 814 if chick.abode:
751 chick.abode.clear_occupant() 815 chick.abode.clear_occupant()