changeset 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 c34d53edfa12
children 4b8ca482ea08
files gamelib/gameboard.py gamelib/toolbar.py
diffstat 2 files changed, 156 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/gamelib/gameboard.py	Wed Nov 25 14:24:35 2009 +0000
+++ b/gamelib/gameboard.py	Wed Nov 25 14:32:40 2009 +0000
@@ -102,7 +102,6 @@
         self.calculate_wood_groat_exchange_rate()
 
         self.selected_tool = None
-        self.animal_to_place = None
         self.sprite_cursor = None
         self.chickens = set()
         self.foxes = set()
@@ -129,6 +128,8 @@
 
         self.tv.run_codes(cdata, (0,0,width,height))
 
+        self.selected_chickens = []
+
     def get_top_widget(self):
         return self.top_widget
 
@@ -175,18 +176,38 @@
 
     def set_selected_tool(self, tool, cursor):
         if not self.day:
-            return
+            return False
+        if self.apply_tool_to_selected(tool):
+            return False # Using the tool on selected chickens is immediate
         self.selected_tool = tool
-        if self.animal_to_place:
-            # Clear any highlights
-            self.animal_to_place.unequip_by_name("Spotlight")
-        self.select_animal_to_place(None)
+        if tool in [None, constants.TOOL_SELL_BUILDING,
+                constants.TOOL_REPAIR_BUILDING]:
+            # FIXME - this special casing is sucky
+            self.unselect_all()
         sprite_curs = None
         if buildings.is_building(tool):
             sprite_curs = sprite_cursor.SpriteCursor(tool.IMAGE, self.tv, tool.BUY_PRICE)
         elif equipment.is_equipment(tool):
             sprite_curs = sprite_cursor.SpriteCursor(tool.CHICKEN_IMAGE_FILE, self.tv)
         self.set_cursor(cursor, sprite_curs)
+        return True
+
+    def apply_tool_to_selected(self, tool):
+        if self.selected_chickens:
+            # dispatch call to selected chickens if appropriate
+            if tool == constants.TOOL_SELL_CHICKEN:
+                self.sell_chicken(None)
+                return True
+            elif tool == constants.TOOL_SELL_EGG:
+                self.sell_egg(None)
+                return True
+            elif tool == constants.TOOL_SELL_EQUIPMENT:
+                self.sell_equipment(None)
+                return True
+            elif equipment.is_equipment(tool):
+                self.buy_equipment(None, tool)
+                return True
+        return False
 
     def set_cursor(self, cursor=None, sprite_curs=None):
         if cursor:
@@ -255,6 +276,8 @@
             self.sell_egg(self.tv.screen_to_tile(e.pos))
         elif self.selected_tool == constants.TOOL_PLACE_ANIMALS:
             self.place_animal(self.tv.screen_to_tile(e.pos))
+        elif self.selected_tool == constants.TOOL_SELECT_CHICKENS:
+            self.select_chicken(self.tv.screen_to_tile(e.pos))
         elif self.selected_tool == constants.TOOL_SELL_BUILDING:
             self.sell_building(self.tv.screen_to_tile(e.pos))
         elif self.selected_tool == constants.TOOL_SELL_EQUIPMENT:
@@ -264,7 +287,10 @@
         elif buildings.is_building(self.selected_tool):
             self.buy_building(self.tv.screen_to_tile(e.pos), self.selected_tool)
         elif equipment.is_equipment(self.selected_tool):
-            self.buy_equipment(self.tv.screen_to_tile(e.pos), self.selected_tool)
+            if not self.selected_chickens:
+                # old selection behaviour
+                self.buy_equipment(self.tv.screen_to_tile(e.pos),
+                        self.selected_tool)
 
     def get_outside_chicken(self, tile_pos):
         for chick in self.chickens:
@@ -297,13 +323,17 @@
             self.remove_chicken(chicken)
             return True
 
-        chick = self.get_outside_chicken(tile_pos)
-        if chick is None:
-            building = self.get_building(tile_pos)
-            if building and building.HENHOUSE:
-                self.open_building_dialog(building, do_sell)
-            return
-        do_sell(chick)
+        if tile_pos:
+            chick = self.get_outside_chicken(tile_pos)
+            if chick is None:
+                building = self.get_building(tile_pos)
+                if building and building.HENHOUSE:
+                    self.open_building_dialog(building, do_sell)
+                return
+            do_sell(chick)
+        else:
+            for chick in self.selected_chickens[:]:
+                do_sell(chick)
 
     def sell_one_egg(self, chicken):
         if chicken.eggs:
@@ -329,12 +359,44 @@
         if building and building.HENHOUSE:
             self.open_building_dialog(building, do_sell)
 
-    def select_animal_to_place(self, animal):
-        if self.animal_to_place:
-            self.animal_to_place.unequip_by_name("Spotlight")
-        self.animal_to_place = animal
-        if self.animal_to_place:
-            self.animal_to_place.equip(equipment.Spotlight())
+    def select_animal(self, animal, extend=True):
+        if extend:
+            self.selected_chickens.append(animal)
+            animal.equip(equipment.Spotlight())
+        else:
+            self.unselect_all()
+            self.selected_chickens.append(animal)
+            animal.equip(equipment.Spotlight())
+
+    def unselect_all(self):
+        # Clear any highlights
+        for chick in self.selected_chickens:
+            chick.unequip_by_name("Spotlight")
+        self.selected_chickens = []
+
+    def unselect_animal(self, animal):
+        if animal in self.selected_chickens:
+            self.selected_chickens.remove(animal)
+            animal.unequip_by_name("Spotlight")
+
+    def get_chicken_at_pos(self, tile_pos):
+        chicken = self.get_outside_chicken(tile_pos)
+        if chicken:
+            return chicken
+        building = self.get_building(tile_pos)
+        if building:
+            self.open_building_dialog(building)
+
+    def select_chicken(self, tile_pos):
+        """Handle a select chicken event"""
+        # Get the chicken at this position
+        chicken = self.get_chicken_at_pos(tile_pos)
+        if not chicken:
+            return # do nothing
+        elif chicken in self.selected_chickens:
+            self.unselect_animal(chicken)
+        else:
+            self.select_animal(chicken)
 
     def place_animal(self, tile_pos):
         """Handle an TOOL_PLACE_ANIMALS click.
@@ -342,36 +404,36 @@
            This will either select an animal or
            place a selected animal in a building.
            """
-        chicken = self.get_outside_chicken(tile_pos)
-        if chicken:
-            if chicken is self.animal_to_place:
-                self.select_animal_to_place(None)
-                pygame.mouse.set_cursor(*cursors.cursors['select'])
-            else:
-                self.select_animal_to_place(chicken)
+        if tile_pos and not self.selected_chickens:
+            # Old behaviour
+            chicken = self.get_chicken_at_pos(tile_pos)
+            if chicken:
+                self.select_animal(chicken)
                 pygame.mouse.set_cursor(*cursors.cursors['chicken'])
-            return
-        building = self.get_building(tile_pos)
-        if building and building.ABODE:
-            if self.animal_to_place:
-                try:
-                    place = building.first_empty_place()
-                    self.relocate_animal(self.animal_to_place, place=place)
-                    self.animal_to_place.equip(equipment.Nest())
-                    self.select_animal_to_place(None)
-                    pygame.mouse.set_cursor(*cursors.cursors['select'])
-                except buildings.BuildingFullError:
-                    pass
-            else:
-                self.open_building_dialog(building)
-            return
-        if self.tv.get(tile_pos) == self.GRASSLAND:
-            if self.animal_to_place is not None:
-                self.animal_to_place.unequip_by_name("Nest")
-                self.relocate_animal(self.animal_to_place, tile_pos=tile_pos)
-                self.eggs -= self.animal_to_place.get_num_eggs()
-                self.animal_to_place.remove_eggs()
-                self.toolbar.update_egg_counter(self.eggs)
+                return
+        elif tile_pos:
+            building = self.get_building(tile_pos)
+            if building and building.ABODE:
+                for chicken in self.selected_chickens:
+                    try:
+                        place = building.first_empty_place()
+                        self.relocate_animal(chicken, place=place)
+                        chicken.equip(equipment.Nest())
+                        pygame.mouse.set_cursor(*cursors.cursors['select'])
+                    except buildings.BuildingFullError:
+                        pass
+                else:
+                    self.open_building_dialog(building)
+                return
+            if self.tv.get(tile_pos) == self.GRASSLAND:
+                for chicken in self.selected_chickens:
+                    # FIXME: find free square nearby
+                    if not self.get_outside_chicken(tile_pos):
+                        chicken.unequip_by_name("Nest")
+                        self.relocate_animal(chicken, tile_pos=tile_pos)
+                        self.eggs -= chicken.get_num_eggs()
+                        chicken.remove_eggs()
+                        self.toolbar.update_egg_counter(self.eggs)
 
     def relocate_animal(self, chicken, tile_pos=None, place=None):
         assert((tile_pos, place) != (None, None))
@@ -439,23 +501,20 @@
             if place.occupant:
                 # there is an occupant, select or sell it
                 if not sell_callback:
-                    old_animal = self.animal_to_place
-                    self.select_animal_to_place(place.occupant)
-                    # deselect old animal (on button)
-                    update_button(old_animal)
+                    self.select_animal(place.occupant)
                     # select new animal (on button)
-                    update_button(self.animal_to_place)
+                    update_button(place.occupant)
                 else:
                     # Attempt to sell the occupant
                     sell_callback(place.occupant, update_button)
             else:
                 # there is no occupant, attempt to fill the space
-                if self.animal_to_place is not None:
+                if self.selected_chickens:
                     # empty old nest (on button)
-                    update_button(self.animal_to_place, empty=True)
-                    self.relocate_animal(self.animal_to_place, place=place)
+                    update_button(self.selected_chickens[0], empty=True)
+                    self.relocate_animal(self.selected_chickens[0], place=place)
                     # populate the new nest (on button)
-                    update_button(self.animal_to_place)
+                    update_button(self.selected_chickens[0])
 
         tbl = gui.Table()
         columns = building.max_floor_width()
@@ -480,15 +539,16 @@
             building.selected(False)
 
         def evict_callback():
-            if not self.animal_to_place:
+            if not self.selected_chickens:
                 return
-            for tile_pos in building.adjacent_tiles():
-                if self.tv.get(tile_pos) != self.GRASSLAND:
-                    continue
-                if self.get_outside_chicken(tile_pos) is None:
-                    update_button(self.animal_to_place, empty=True)
-                    self.place_animal(tile_pos)
-                    break
+            for chicken in self.selected_chickens:
+                for tile_pos in building.adjacent_tiles():
+                    if self.tv.get(tile_pos) != self.GRASSLAND:
+                        continue
+                    if self.get_outside_chicken(tile_pos) is None:
+                        update_button(chicken, empty=True)
+                        self.place_animal(tile_pos)
+                        break
 
         if not sell_callback:
             tbl.tr()
@@ -523,15 +583,19 @@
                     update_button(chicken)
             return False
 
-        chicken = self.get_outside_chicken(tile_pos)
-        if chicken is None:
-            building = self.get_building(tile_pos)
-            if not (building and building.ABODE):
-                return
-            # Bounce through open dialog once more
-            self.open_building_dialog(building, do_equip)
+        if tile_pos:
+            chicken = self.get_outside_chicken(tile_pos)
+            if chicken is None:
+                building = self.get_building(tile_pos)
+                if not (building and building.ABODE):
+                     return
+                # Bounce through open dialog once more
+                self.open_building_dialog(building, do_equip)
+            else:
+                do_equip(chicken)
         else:
-            do_equip(chicken)
+            for chicken in self.selected_chickens:
+                do_equip(chicken)
 
     def sell_building(self, tile_pos):
         building = self.get_building(tile_pos)
@@ -742,8 +806,8 @@
             self.tv.sprites.remove(fox)
 
     def remove_chicken(self, chick):
-        if chick is self.animal_to_place:
-            self.select_animal_to_place(None)
+        if chick in self.selected_chickens:
+            self.unselect_animal(chick)
         self.chickens.discard(chick)
         self.eggs -= chick.get_num_eggs()
         self.toolbar.update_egg_counter(self.eggs)
--- a/gamelib/toolbar.py	Wed Nov 25 14:24:35 2009 +0000
+++ b/gamelib/toolbar.py	Wed Nov 25 14:32:40 2009 +0000
@@ -10,6 +10,17 @@
 import engine
 import version
 
+class RinkhalsTool(gui.Tool):
+    def __init__(self, group, label, value, func, **params):
+        gui.Tool.__init__(self, group, label, value, **params)
+        self.func = func
+
+    def click(self):
+        gui.Tool.click(self)
+        if not self.func():
+            # Don't hightlight if the function says so
+            self.group.value = None
+
 class OpaqueLabel(gui.Label):
     def __init__(self, value, **params):
         gui.Label.__init__(self, value, **params)
@@ -230,8 +241,9 @@
         label = gui.basic.Label(text)
         value = self._next_tool_value
         self._next_tool_value += 1
-        tool = gui.Tool(self.group, label, value, width=self.rect.w, style={"padding_left": 0})
-        tool.connect(gui.CLICK, func)
+        tool = RinkhalsTool(self.group, label, value, func, width=self.rect.w,
+                style={"padding_left": 0})
+        #tool.connect(gui.CLICK, func)
         self.tr()
         self.td(tool, align=-1, colspan=2)
         return tool
@@ -301,6 +313,7 @@
     def add_building_toolbar(self):
         self.gameboard.change_toolbar(BuildingToolBar(self.gameboard,
                 width=self.style.width))
+        self.gameboard.unselect_all()
 
     def add_sell_toolbar(self):
         self.gameboard.change_toolbar(SellToolBar(self.gameboard,
@@ -309,6 +322,7 @@
     def add_wood_toolbar(self):
         self.gameboard.change_toolbar(WoodToolBar(self.gameboard,
                 width=self.style.width))
+        self.gameboard.unselect_all()
 
     def add_equipment_toolbar(self):
         self.gameboard.change_toolbar(EquipmentToolBar(self.gameboard,