changeset 125:2e3a05b9594d

Chickens in buildings\!
author Simon Cross <hodgestar@gmail.com>
date Wed, 02 Sep 2009 21:28:04 +0000
parents 69fd96eafde8
children 73e13216d7bb
files gamelib/animal.py gamelib/buildings.py gamelib/gameboard.py
diffstat 3 files changed, 144 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/gamelib/animal.py	Wed Sep 02 21:14:05 2009 +0000
+++ b/gamelib/animal.py	Wed Sep 02 21:28:04 2009 +0000
@@ -57,6 +57,9 @@
     def covers(self, tile_pos):
         return tile_pos[0] == self.pos.x and tile_pos[1] == self.pos.y
 
+    def outside(self):
+        return self.abode is None
+
 class Chicken(Animal):
     """A chicken"""
 
@@ -114,9 +117,6 @@
             sound.play_sound("kill-fox.ogg")
             gameboard.kill_fox(fox)
 
-    def outside(self):
-        return self.abode is None
-
 class Egg(Animal):
     """An egg"""
 
--- a/gamelib/buildings.py	Wed Sep 02 21:14:05 2009 +0000
+++ b/gamelib/buildings.py	Wed Sep 02 21:28:04 2009 +0000
@@ -5,6 +5,47 @@
 import imagecache
 import tiles
 
+class Place(object):
+    """Space within a building that can be occupied."""
+
+    def __init__(self, building, offset):
+        self.occupant = None
+        self.building = building
+        self.offset = offset
+
+    def set_occupant(self, occupant):
+        self.clear_occupant()
+        self.occupant = occupant
+        self.occupant.abode = self
+
+    def clear_occupant(self):
+        if self.occupant is not None:
+            self.occupant.abode = None
+            self.occupant = None
+
+    def get_pos(self):
+        bpos = self.building.pos
+        return (bpos[0] + self.offset[0], bpos[1] + self.offset[1])
+
+class Floor(object):
+    """A set of places within a building. Places on a
+       floor are organised into rows and columns.
+       """
+
+    def __init__(self, title, places):
+        self.title = title # str
+        self.places = places # list of lists of places
+
+    def rows(self):
+        for row in self.places:
+            yield row
+
+    def width(self):
+        return max(len(row) for row in self.places)
+
+class BuildingFullError(Exception):
+    pass
+
 class Building(Sprite):
     """Base class for buildings"""
 
@@ -20,7 +61,17 @@
         self.tile_no = self.TILE_NO
         self._buy_price = self.BUY_PRICE
         self._sell_price = self.SELL_PRICE
-        self._occupants = set()
+
+        self._floors = []
+        for f in range(self.FLOORS):
+            places = []
+            for i in range(self.size[0]):
+                row = []
+                for j in range(self.size[1]):
+                    row.append(Place(self, (i, j)))
+                places.append(row)
+            floor = Floor("Floor %s" % (f+1,), places)
+            self._floors.append(floor)
 
         # Create the building somewhere far off screen
         Sprite.__init__(self, self.day_image, (-1000, -1000))
@@ -86,20 +137,32 @@
         else:
             self.setimage(self.night_image)
 
-    def occupants(self):
-        """Return list of buildings occupants."""
-        return list(self._occupants)
+    def floors(self):
+        return self._floors
+
+    def places(self):
+        for floor in self._floors:
+            for row in floor.rows():
+                for place in row:
+                    yield place
+
+    def max_floor_width(self):
+        return max(floor.width() for floor in self._floors)
+
+    def first_empty_place(self):
+        for place in self.places():
+            if place.occupant is None:
+                return place
+        raise BuildingFullError()
 
     def add_occupant(self, occupant):
-        if occupant.abode is not None:
-            occupant.abode.remove_occupant(occupant)
-        occupant.abode = self
-        self._occupants.add(occupant)
+        place = self.first_empty_place()
+        place.set_occupant(occupant)
 
-    def remove_occupant(self, occupant):
-        if occupant in self._occupants:
-            self._occupants.remove(occupant)
-            occupant.abode = None
+    def occupants(self):
+        for place in self.places():
+            if place.occupant is not None:
+                yield place.occupant
 
 class HenHouse(Building):
     """A HenHouse."""
@@ -110,7 +173,14 @@
     SIZE = (3, 2)
     IMAGE = 'sprites/henhouse.png'
     NAME = 'Hen House'
+    FLOORS = 1
 
+class DoubleStoryHenHouse(HenHouse):
+    """A double story hen house."""
+    BUY_PRICE = 300
+    SELL_PRICE = 150
+    NAME = 'Hendominium'
+    FLOORS = 2
 
 class GuardTower(Building):
     """A GuardTower."""
@@ -121,6 +191,7 @@
     SIZE = (2, 2)
     IMAGE = 'sprites/watchtower.png'
     NAME = 'Watch Tower'
+    FLOORS = 1
 
 def is_building(obj):
     """Return true if obj is a build class."""
--- a/gamelib/gameboard.py	Wed Sep 02 21:14:05 2009 +0000
+++ b/gamelib/gameboard.py	Wed Sep 02 21:28:04 2009 +0000
@@ -233,24 +233,26 @@
                 self.animal_to_place = None
             else:
                 self.animal_to_place = chicken
-            print "Selected animal %r" % (self.animal_to_place,)
             return
         building = self.get_building(tile_pos)
         if building:
-            # XXX: quick hack so egg loop triggers
-            if self.animal_to_place:
-                building.add_occupant(self.animal_to_place)
-                if self.animal_to_place in self.tv.sprites:
-                    self.tv.sprites.remove(self.animal_to_place)
-                self.animal_to_place = None
-                self.open_building_dialog(building)
+            self.open_building_dialog(building)
             return
         if self.tv.get(tile_pos) == self.GRASSLAND:
             if self.animal_to_place is not None:
                 occupant = self.animal_to_place
                 if occupant.abode is not None:
-                    occupant.abode.remove_occupant(occupant)
+                    occupant.abode.clear_occupant()
                 occupant.set_pos(tile_pos)
+                self.set_visibility(occupant)
+
+    def set_visibility(self, chicken):
+        if chicken.outside():
+            if chicken not in self.tv.sprites:
+                self.tv.sprites.append(chicken)
+        else:
+            if chicken in self.tv.sprites:
+                self.tv.sprites.remove(chicken)
 
     def open_dialog(self, widget):
         """Open a dialog for the given widget. Add close button."""
@@ -273,13 +275,50 @@
 
     def open_building_dialog(self, building):
         """Create dialog for manipulating the contents of a building."""
+        def select_occupant(place, button):
+            """Select occupant in place."""
+            self.animal_to_place = place.occupant
+
+        def set_occupant(place, button):
+            """Set occupant of a given place."""
+            if self.animal_to_place is not None:
+                button.value = icons.CHKN_NEST_ICON
+                button.disconnect(gui.CLICK, set_occupant)
+                button.connect(gui.CLICK, select_occupant, place, button)
+
+                old_abode = self.animal_to_place.abode
+                if id(old_abode) in place_button_map:
+                    old_button = place_button_map[id(old_abode)]
+                    old_button.value = icons.EMPTY_NEST_ICON
+                    old_button.disconnect(gui.CLICK, select_occupant)
+                    old_button.connect(gui.CLICK, set_occupant, place, button)
+
+                chicken = self.animal_to_place
+                place.set_occupant(chicken)
+                chicken.set_pos(place.get_pos())
+                self.set_visibility(self.animal_to_place)
+
+        place_button_map = {}
+
         width, height = pygame.display.get_surface().get_size()
         tbl = gui.Table()
-        for row in range(building.size[0]):
+        columns = building.max_floor_width()
+        kwargs = { 'style': { 'padding_left': 10, 'padding_bottom': 10 }}
+        for floor in building.floors():
+            tbl.tr()
+            tbl.td(gui.Button(floor.title), colspan=columns, align=-1, **kwargs)
             tbl.tr()
-            for col in range(building.size[1]):
-                button = gui.Button("%s, %s" % (row, col))
-                tbl.td(button)
+            for row in floor.rows():
+                tbl.tr()
+                for place in row:
+                    if place.occupant is None:
+                        button = gui.Button(icons.EMPTY_NEST_ICON)
+                        button.connect(gui.CLICK, set_occupant, place, button)
+                    else:
+                        button = gui.Button(icons.CHKN_NEST_ICON)
+                        button.connect(gui.CLICK, select_occupant, place, button)
+                    place_button_map[id(place)] = button
+                    tbl.td(button, **kwargs)
 
         self.open_dialog(tbl)
 
@@ -401,8 +440,11 @@
                     new_chick = chicken.hatch()
                     if new_chick:
                         self.eggs -= 1
-                        building.add_occupant(new_chick)
-                        self.add_chicken(new_chick)
+                        try:
+                            building.add_occupant(new_chick)
+                            self.add_chicken(new_chick)
+                        except buildings.BuildingFullError:
+                            print "Building full."
         self.toolbar.update_egg_counter(self.eggs)
 
     def kill_fox(self, fox):
@@ -423,7 +465,7 @@
             self.eggs -= 1
             self.toolbar.update_egg_counter(self.eggs)
         if chick.abode:
-            chick.abode.remove_occupant(chick)
+            chick.abode.clear_occupant()
         self.toolbar.update_chicken_counter(len(self.chickens))
         if chick in self.tv.sprites:
             if chick.outside():