changeset 476:3dae0fc14009

Animals have their own gameboard references instead of lugging them around in params all the time.
author Jeremy Thurgood <firxen@gmail.com>
date Wed, 25 Nov 2009 17:44:32 +0000
parents d4f04d81fe54
children c1439f6705a2
files gamelib/animal.py gamelib/gameboard.py
diffstat 2 files changed, 117 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
--- a/gamelib/animal.py	Wed Nov 25 17:08:17 2009 +0000
+++ b/gamelib/animal.py	Wed Nov 25 17:44:32 2009 +0000
@@ -32,7 +32,7 @@
         'facing',
     ]
 
-    def __init__(self, tile_pos):
+    def __init__(self, tile_pos, gameboard):
         # load images
         self._image_left = imagecache.load_image(self.IMAGE_FILE)
         self._image_right = imagecache.load_image(self.IMAGE_FILE, ("right_facing",))
@@ -48,6 +48,7 @@
         self.accoutrements = []
         self.abode = None
         self.facing = 'left'
+        self.gameboard = gameboard
 
     def make(cls):
         """Override default Simplifiable object creation."""
@@ -66,15 +67,15 @@
         self.rect.x = ppos[0]
         self.rect.y = ppos[1]
 
-    def die(self, gameboard):
+    def die(self):
         """Play death animation, noises, whatever."""
         if hasattr(self, 'DEATH_SOUND'):
             sound.play_sound(self.DEATH_SOUND)
         if hasattr(self, 'DEATH_ANIMATION'):
-            self.DEATH_ANIMATION(gameboard.tv, self.pos.to_tile_tuple())
-        self._game_death(gameboard)
+            self.DEATH_ANIMATION(self.gameboard.tv, self.pos.to_tile_tuple())
+        self._game_death()
 
-    def _game_death(self, gameboard):
+    def _game_death(self):
         # Call appropriate gameboard cleanup here.
         pass
 
@@ -83,7 +84,7 @@
         # Default is not to move
         pass
 
-    def attack(self, gameboard):
+    def attack(self):
         """Given the game state, attack a suitable target"""
         # Default is not to attack
         pass
@@ -159,12 +160,12 @@
     def outside(self):
         return self.abode is None
 
-    def damage(self, gameboard):
+    def damage(self):
         for a in self.armour():
             if not a.survive_damage():
                 self.unequip(a)
             return True
-        self.die(gameboard)
+        self.die()
         return False
 
 class Chicken(Animal):
@@ -177,107 +178,106 @@
 
     SIMPLIFY = Animal.SIMPLIFY + ['eggs']
 
-    def __init__(self, pos):
-        Animal.__init__(self, pos)
+    def __init__(self, pos, gameboard):
+        Animal.__init__(self, pos, gameboard)
         self.eggs = []
 
-    def start_night(self, gameboard):
-        self.lay(gameboard)
+    def start_night(self):
+        self.lay()
         self.reload_weapon()
 
-    def start_day(self, gameboard):
-        self.hatch(gameboard)
+    def start_day(self):
+        self.hatch()
 
-    def _game_death(self, gameboard):
-        gameboard.remove_chicken(self)
+    def _game_death(self):
+        self.gameboard.remove_chicken(self)
 
-    def move(self, gameboard):
+    def move(self):
         """A free chicken will wander around aimlessly"""
         pos_x, pos_y = self.pos.to_tile_tuple()
         surrounds = [Position(pos_x + dx, pos_y + dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1]]
-        pos_options = [pos for pos in surrounds if gameboard.in_bounds(pos) and gameboard.tv.get(pos.to_tile_tuple()) == gameboard.GRASSLAND and not gameboard.get_outside_chicken(pos.to_tile_tuple())] + [self.pos]
+        pos_options = [pos for pos in surrounds if self.gameboard.in_bounds(pos) and self.gameboard.tv.get(pos.to_tile_tuple()) == self.gameboard.GRASSLAND and not self.gameboard.get_outside_chicken(pos.to_tile_tuple())] + [self.pos]
         self.pos = pos_options[random.randint(0, len(pos_options)-1)]
 
     def has_axe(self):
         return bool([e for e in self.weapons() if e.TYPE == "AXE"])
 
-    def chop(self, gameboard):
+    def chop(self):
         if self.has_axe():
             pos_x, pos_y = self.pos.to_tile_tuple()
             surrounds = [Position(pos_x + dx, pos_y + dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1]]
-            tree_options = [pos for pos in surrounds if gameboard.in_bounds(pos) and gameboard.tv.get(pos.to_tile_tuple()) == gameboard.WOODLAND]
+            tree_options = [pos for pos in surrounds if self.gameboard.in_bounds(pos) and self.gameboard.tv.get(pos.to_tile_tuple()) == self.gameboard.WOODLAND]
             if tree_options:
                 num_trees_to_cut = random.randint(1, len(tree_options))
                 trees_to_cut = random.sample(tree_options, num_trees_to_cut)
                 for tree_pos in trees_to_cut:
-                    gameboard.add_wood(5)
-                    gameboard.tv.set(tree_pos.to_tile_tuple(), gameboard.GRASSLAND)
+                    self.gameboard.add_wood(5)
+                    self.gameboard.tv.set(tree_pos.to_tile_tuple(), self.gameboard.GRASSLAND)
 
-    def lay(self, gameboard):
+    def lay(self):
         """See if the chicken lays an egg"""
         if self.abode and self.abode.building.HENHOUSE:
             if not self.eggs:
                 for x in range(random.randint(1, 4)):
-                    self.eggs.append(Egg(self.pos))
+                    self.eggs.append(Egg(self.pos, self.gameboard))
                 self.equip(equipment.NestEgg())
-            gameboard.eggs += self.get_num_eggs()
+            self.gameboard.eggs += self.get_num_eggs()
 
-    def remove_eggs(self, gameboard):
+    def remove_eggs(self):
         """Clean up the egg state"""
-        gameboard.remove_eggs(len(self.eggs))
+        self.gameboard.remove_eggs(len(self.eggs))
         self.eggs = []
         self.unequip_by_name("Nestegg")
 
-    def remove_one_egg(self, gameboard):
+    def remove_one_egg(self):
         """Clean up the egg state"""
         self.eggs.pop()
-        gameboard.remove_eggs(1)
+        self.gameboard.remove_eggs(1)
         if not self.eggs:
             self.unequip_by_name("Nestegg")
 
     def get_num_eggs(self):
         return len(self.eggs)
 
-    def hatch(self, gameboard):
+    def hatch(self):
         """See if we have an egg to hatch"""
         if self.eggs:
             chick = self.eggs[0].hatch()
             if chick:
                 # sell the remaining eggs
                 # Remove hatched egg
-                self.eggs.pop() 
-                gameboard.eggs -= 1
+                self.remove_one_egg()
                 # Sell other eggs
                 for egg in self.eggs[:]:
-                    gameboard.sell_one_egg(self)
-                self.remove_eggs(gameboard) # clean up stale images, etc.
-                gameboard.place_hatched_chicken(chick, self.abode.building)
+                    self.gameboard.sell_one_egg(self)
+                self.remove_eggs() # clean up stale images, etc.
+                self.gameboard.place_hatched_chicken(chick, self.abode.building)
 
-    def _find_killable_fox(self, weapon, gameboard):
+    def _find_killable_fox(self, weapon):
         """Choose a random fox within range of this weapon."""
         killable_foxes = []
-        for fox in gameboard.foxes:
-            if not weapon.in_range(gameboard, self, fox):
+        for fox in self.gameboard.foxes:
+            if not weapon.in_range(self.gameboard, self, fox):
                 continue
-            if visible(self, fox, gameboard):
+            if visible(self, fox, self.gameboard):
                 killable_foxes.append(fox)
         if not killable_foxes:
             return None
         return random.choice(killable_foxes)
 
-    def attack(self, gameboard):
+    def attack(self):
         """An armed chicken will attack a fox within range."""
         if not self.weapons():
             # Not going to take on a fox bare-winged.
             return
         # Choose the first weapon equipped.
         weapon = self.weapons()[0]
-        fox = self._find_killable_fox(weapon, gameboard)
+        fox = self._find_killable_fox(weapon)
         if not fox:
             return
         self._fix_face(fox.pos)
-        if weapon.hit(gameboard, self, fox):
-            fox.damage(gameboard)
+        if weapon.hit(self.gameboard, self, fox):
+            fox.damage()
 
     def reload_weapon(self):
         """If we have a weapon that takes ammunition, reload it."""
@@ -291,8 +291,8 @@
 
     SIMPLIFY = Animal.SIMPLIFY + ['timer']
 
-    def __init__(self, pos):
-        Animal.__init__(self, pos)
+    def __init__(self, pos, gameboard):
+        Animal.__init__(self, pos, gameboard)
         self.timer = 2
 
     # Eggs don't move
@@ -300,7 +300,7 @@
     def hatch(self):
         self.timer -= 1
         if self.timer == 0:
-            return Chicken(self.pos)
+            return Chicken(self.pos, self.gameboard)
         return None
 
 class Fox(Animal):
@@ -324,8 +324,8 @@
             'hendominium' : 30,
             }
 
-    def __init__(self, pos):
-        Animal.__init__(self, pos)
+    def __init__(self, pos, gameboard):
+        Animal.__init__(self, pos, gameboard)
         self.landmarks = [self.pos]
         self.hunting = True
         self.dig_pos = None
@@ -340,22 +340,22 @@
     def outside(self):
         return self.building is None
 
-    def _game_death(self, gameboard):
-        gameboard.kill_fox(self)
+    def _game_death(self):
+        self.gameboard.kill_fox(self)
 
-    def _cost_tile(self, pos, gameboard):
-        if gameboard.in_bounds(pos):
-            this_tile = gameboard.tv.get(pos.to_tile_tuple())
+    def _cost_tile(self, pos):
+        if self.gameboard.in_bounds(pos):
+            this_tile = self.gameboard.tv.get(pos.to_tile_tuple())
             cost = self.costs.get(tiles.TILE_MAP[this_tile], 100)
         else:
             cost = 100 # Out of bounds is expensive
         return cost
 
-    def _cost_path(self, path, gameboard):
+    def _cost_path(self, path):
         """Calculate the cost of a path"""
         total = 0
         for pos in path:
-            total += self._cost_tile(pos, gameboard)
+            total += self._cost_tile(pos)
         return total
 
     def _gen_path(self, start_pos, final_pos):
@@ -363,7 +363,7 @@
            excluding start_pos"""
         return start_pos.intermediate_positions(final_pos)
 
-    def _find_best_path_step(self, final_pos, gameboard):
+    def _find_best_path_step(self, final_pos):
         """Find the cheapest path to final_pos, and return the next step
            along the path."""
         # We calculate the cost of the direct path
@@ -387,19 +387,19 @@
         for point in neighbours:
             dist = point.dist(final_pos)
             if dist < cur_dist:
-                cost = self._cost_tile(point, gameboard)
+                cost = self._cost_tile(point)
                 if cost < min_cost or (min_cost == cost and dist < min_dist):
                     # Prefer closest of equal cost points
                     min_dist = dist
                     min_cost = cost
                     best = point
-        if min_cost < 20 or not gameboard.in_bounds(self.pos):
+        if min_cost < 20 or not self.gameboard.in_bounds(self.pos):
             # If we're not on the gameboard yet, there's no point in looking
             # for an optimal path.
             return best
         # Else expensive step, so think further
         direct_path = self._gen_path(self.pos, final_pos)
-        min_cost = self._cost_path(direct_path, gameboard)
+        min_cost = self._cost_path(direct_path)
         min_path = direct_path
         # is there a point nearby that gives us a cheaper direct path?
         # This is delibrately not finding the optimal path, as I don't
@@ -412,7 +412,7 @@
         for start in poss:
             cand_path = self._gen_path(self.pos, start) + \
                     self._gen_path(start, final_pos)
-            cost = self._cost_path(cand_path, gameboard)
+            cost = self._cost_path(cand_path)
             if cost < min_cost:
                 min_cost = cost
                 min_path = cand_path
@@ -420,22 +420,22 @@
             return final_pos
         return min_path[0]
 
-    def _find_path_to_woodland(self, gameboard):
+    def _find_path_to_woodland(self):
         """Dive back to woodland through the landmarks"""
         # find the closest point to our current location in walked path
         if self.pos == self.landmarks[-1]:
             if len(self.landmarks) > 1:
                 self.landmarks.pop() # Moving to the next landmark
-        if not gameboard.in_bounds(self.pos) and not self.hunting:
+        if not self.gameboard.in_bounds(self.pos) and not self.hunting:
             # Safely out of sight
             self.safe = True
             return self.pos
-        return self._find_best_path_step(self.landmarks[-1], gameboard)
+        return self._find_best_path_step(self.landmarks[-1])
 
-    def _select_target(self, gameboard):
+    def _select_target(self):
         min_dist = 999
         self.closest = None
-        for chicken in gameboard.chickens:
+        for chicken in self.gameboard.chickens:
             dist = chicken.pos.dist(self.pos)
             if chicken.abode:
                 dist += 5 # Prefer free-ranging chickens
@@ -445,12 +445,12 @@
                 min_dist = dist
                 self.closest = chicken
 
-    def _find_path_to_chicken(self, gameboard):
+    def _find_path_to_chicken(self):
         """Find the path to the closest chicken"""
         # Find the closest chicken
-        if self.closest not in gameboard.chickens:
+        if self.closest not in self.gameboard.chickens:
             # Either no target, or someone ate it
-            self._select_target(gameboard)
+            self._select_target()
         if not self.closest:
             # No more chickens, so leave
             self.hunting = False
@@ -465,28 +465,28 @@
             else:
                 new_z = self.pos.z + 1
             return Position(self.pos.x, self.pos.y, new_z)
-        return self._find_best_path_step(self.closest.pos, gameboard)
+        return self._find_best_path_step(self.closest.pos)
 
-    def attack(self, gameboard):
+    def attack(self):
         """Attack a chicken"""
-        chicken = gameboard.get_animal_at_pos(self.pos, 'chicken')
+        chicken = self.gameboard.get_animal_at_pos(self.pos, 'chicken')
         if chicken:
             # Always attack a chicken we step on, even if not hunting
-            self._catch_chicken(chicken, gameboard)
+            self._catch_chicken(chicken)
 
-    def _catch_chicken(self, chicken, gameboard):
+    def _catch_chicken(self, chicken):
         """Catch a chicken"""
-        chicken.damage(gameboard)
+        chicken.damage()
         self.closest = None
         self.hunting = False
         self.last_steps = [] # Forget history here
 
-    def _update_pos(self, gameboard, new_pos):
+    def _update_pos(self, new_pos):
         """Update the position, making sure we don't step on other foxes"""
         if new_pos == self.pos:
             # We're not moving, so we can skip all the checks
             return new_pos
-        blocked = gameboard.get_animal_at_pos(new_pos, 'fox') is not None
+        blocked = self.gameboard.get_animal_at_pos(new_pos, 'fox') is not None
         if not blocked and new_pos.z == self.pos.z:
             # We're only worried about loops when not on a ladder
             blocked = new_pos in self.last_steps
@@ -506,9 +506,9 @@
             final_pos = None
             min_cost = 1000
             for poss in moves:
-                if gameboard.get_animal_at_pos(poss, 'fox'):
+                if self.gameboard.get_animal_at_pos(poss, 'fox'):
                     continue # blocked
-                cost = self._cost_tile(poss, gameboard)
+                cost = self._cost_tile(poss)
                 if cost < min_cost:
                     min_cost = cost
                     final_pos = poss
@@ -518,35 +518,35 @@
         if not final_pos:
             # No good choice, so stay put
             return self.pos
-        if gameboard.in_bounds(final_pos):
-            this_tile = gameboard.tv.get(final_pos.to_tile_tuple())
+        if self.gameboard.in_bounds(final_pos):
+            this_tile = self.gameboard.tv.get(final_pos.to_tile_tuple())
         else:
             this_tile = tiles.REVERSE_TILE_MAP['woodland']
         if tiles.TILE_MAP[this_tile] == 'broken fence' and self.hunting:
             # We'll head back towards the holes we make/find
             self.landmarks.append(final_pos)
         elif tiles.TILE_MAP[this_tile] == 'fence' and not self.dig_pos:
-            return self._dig(gameboard, final_pos)
+            return self._dig(final_pos)
         self.last_steps.append(final_pos)
         if len(self.last_steps) > 3:
             self.last_steps.pop(0)
         return final_pos
 
-    def _dig(self, gameboard, dig_pos):
+    def _dig(self, dig_pos):
         """Setup dig parameters, to be overridden if needed"""
         self.tick = 5
         self.dig_pos = dig_pos
         return self.pos
 
-    def _make_hole(self, gameboard):
+    def _make_hole(self):
         """Make a hole in the fence"""
-        fence = gameboard.get_building(self.dig_pos.to_tile_tuple())
+        fence = self.gameboard.get_building(self.dig_pos.to_tile_tuple())
         # Another fox could have made the same hole this turn
         if fence:
-            fence.damage(gameboard.tv)
+            fence.damage(self.gameboard.tv)
         self.dig_pos = None
 
-    def move(self, gameboard):
+    def move(self):
         """Foxes will aim to move towards the closest henhouse or free
            chicken"""
         if self.safe:
@@ -559,23 +559,23 @@
                 # Check the another fox hasn't dug a hole for us
                 # We're too busy digging to notice if a hole appears nearby,
                 # but we'll notice if the fence we're digging vanishes
-                this_tile = gameboard.tv.get(self.dig_pos.to_tile_tuple())
+                this_tile = self.gameboard.tv.get(self.dig_pos.to_tile_tuple())
                 if tiles.TILE_MAP[this_tile] != 'fence':
                     self.tick = 0
             else:
                 # We've dug through the fence, so make a hole
-                self._make_hole(gameboard)
+                self._make_hole()
             return
         elif self.hunting:
-            desired_pos = self._find_path_to_chicken(gameboard)
+            desired_pos = self._find_path_to_chicken()
         else:
-            desired_pos = self._find_path_to_woodland(gameboard)
-        final_pos = self._update_pos(gameboard, desired_pos)
+            desired_pos = self._find_path_to_woodland()
+        final_pos = self._update_pos(desired_pos)
         self._fix_face(final_pos)
         self.pos = final_pos
         change_visible = False
         # See if we're entering/leaving a building
-        building = gameboard.get_building(final_pos.to_tile_tuple())
+        building = self.gameboard.get_building(final_pos.to_tile_tuple())
         if building and self.outside():
             # Check if we need to enter
             if self.closest and not self.closest.outside() and \
@@ -595,7 +595,7 @@
                 self.building.remove_predator(self)
                 change_visible = True
         if change_visible:
-            gameboard.set_visibility(self)
+            self.gameboard.set_visibility(self)
 
 
 class NinjaFox(Fox):
@@ -615,24 +615,24 @@
     costs = Fox.costs.copy()
     costs['fence'] = 2
 
-    def _dig(self, gameboard, dig_pos):
+    def _dig(self, dig_pos):
         """Setup dig parameters, to be overridden if needed"""
         self.tick = 0 # Costs us nothing to go through a fence.
         self.dig_pos = dig_pos
-        self.DIG_ANIMATION(gameboard.tv, dig_pos.to_tile_tuple())
-        self._make_hole(gameboard)
+        self.DIG_ANIMATION(self.gameboard.tv, dig_pos.to_tile_tuple())
+        self._make_hole()
         return self.pos
 
 class GreedyFox(Fox):
     """Greedy foxes eat more chickens"""
     CONFIG_NAME = 'greedy fox'
 
-    def __init__(self, pos):
-        Fox.__init__(self, pos)
+    def __init__(self, pos, gameboard):
+        Fox.__init__(self, pos, gameboard)
         self.chickens_eaten = 0
 
-    def _catch_chicken(self, chicken, gameboard):
-        chicken.damage(gameboard)
+    def _catch_chicken(self, chicken):
+        chicken.damage()
         self.closest = None
         self.chickens_eaten += 1
         if self.chickens_eaten > 2:
@@ -645,11 +645,11 @@
     IMAGE_FILE = 'sprites/rinkhals.png'
     CONFIG_NAME = 'rinkhals'
 
-    def _select_target(self, gameboard):
+    def _select_target(self):
         """The Rinkhals eats eggs"""
         min_dist = 999
         self.closest = None
-        for chicken in gameboard.chickens:
+        for chicken in self.gameboard.chickens:
             dist = chicken.pos.dist(self.pos)
             if not chicken.eggs:
                 dist += 100 # The closest eggs have to be *far* away to be safe
@@ -657,18 +657,18 @@
                 min_dist = dist
                 self.closest = chicken
 
-    def _catch_chicken(self, chicken, gameboard):
+    def _catch_chicken(self, chicken):
         """The Rinkhals eats eggs, but does not harm chickens"""
-        chicken.remove_eggs(gameboard)
+        chicken.remove_eggs()
         self.closest = None
         self.hunting = False
         self.last_steps = []
 
-    def _dig(self, gameboard, dig_pos):
+    def _dig(self, dig_pos):
         """Snakes ignore fences"""
         return dig_pos
 
-    def damage(self, gameboard):
+    def damage(self):
         """The Rinkhals is invincible!"""
         return True
 
--- a/gamelib/gameboard.py	Wed Nov 25 17:08:17 2009 +0000
+++ b/gamelib/gameboard.py	Wed Nov 25 17:44:32 2009 +0000
@@ -239,7 +239,7 @@
         self.spawn_foxes()
         self.eggs = 0
         for chicken in self.chickens.copy():
-            chicken.start_night(self)
+            chicken.start_night()
         self.toolbar.update_egg_counter(self.eggs)
         self._cache_animal_positions()
 
@@ -252,7 +252,7 @@
         self.advance_day()
         self.clear_foxes()
         for chicken in self.chickens.copy():
-            chicken.start_day(self)
+            chicken.start_day()
         self.redraw_counters()
 
     def in_bounds(self, pos):
@@ -340,7 +340,7 @@
     def sell_one_egg(self, chicken):
         if chicken.eggs:
             self.add_cash(self.level.sell_price_egg)
-            chicken.remove_one_egg(self)
+            chicken.remove_one_egg()
             return True
         return False
 
@@ -453,7 +453,7 @@
                     if try_pos:
                         chicken.unequip_by_name("Nest")
                         self.relocate_animal(chicken, tile_pos=try_pos)
-                        chicken.remove_eggs(self)
+                        chicken.remove_eggs()
 
     def relocate_animal(self, chicken, tile_pos=None, place=None):
         assert((tile_pos, place) != (None, None))
@@ -751,20 +751,20 @@
         """Chickens outside move around randomly a bit"""
         for chicken in [chick for chick in self.chickens if chick.outside()]:
             old_pos = chicken.pos
-            chicken.move(self)
+            chicken.move()
             self._pos_cache.update(old_pos, chicken, 'chicken')
 
     def chickens_chop_wood(self):
         """Chickens with axes chop down trees near them"""
         for chicken in [chick for chick in self.chickens if chick.outside()]:
-            chicken.chop(self)
+            chicken.chop()
         self.calculate_wood_groat_exchange_rate()
 
     def foxes_move(self):
         over = True
         for fox in self.foxes:
             old_pos = fox.pos
-            fox.move(self)
+            fox.move()
             if not fox.safe:
                 over = False
             self._pos_cache.update(old_pos, fox, 'fox')
@@ -772,11 +772,11 @@
 
     def foxes_attack(self):
         for fox in self.foxes:
-            fox.attack(self)
+            fox.attack()
 
     def chickens_attack(self):
         for chicken in self.chickens:
-            chicken.attack(self)
+            chicken.attack()
 
     def add_chicken(self, chicken):
         self.chickens.add(chicken)
@@ -853,7 +853,7 @@
 
     def add_start_chickens(self, _map, tile, value):
         """Add chickens as specified by the code layer"""
-        chick = animal.Chicken((tile.tx, tile.ty))
+        chick = animal.Chicken((tile.tx, tile.ty), self)
         for equip_cls in equipment.EQUIP_MAP[value]:
             item = equip_cls()
             chick.equip(item)
@@ -861,7 +861,7 @@
 
     def _choose_fox(self, (x, y)):
         fox_cls = misc.WeightedSelection(self.level.fox_weightings).choose()
-        return fox_cls((x, y))
+        return fox_cls((x, y), self)
 
     def spawn_foxes(self):
         """The foxes come at night, and this is where they come from."""