Mercurial > fhrl
changeset 20:8e8ec6a3b6e4
Make beasts aggressive
| author | Radomir Dopieralski <sheep-devel@sheep.art.pl> |
|---|---|
| date | Sat, 16 Mar 2013 00:14:14 +0100 |
| parents | 47be6695b781 |
| children | 5edb554f732b |
| files | fhrl/__main__.py fhrl/digits.png fhrl/display.py fhrl/items.png fhrl/level.py fhrl/mobs.py fhrl/mobs2.png fhrl/resources.py fhrl/rocks.png fhrl/stuff.png |
| diffstat | 10 files changed, 192 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/fhrl/__main__.py Fri Mar 15 15:57:03 2013 +0100 +++ b/fhrl/__main__.py Sat Mar 16 00:14:14 2013 +0100 @@ -9,7 +9,7 @@ class Game(object): def __init__(self): - self.margin = 64 + self.margin = 128 self.display = Display() self.level = Level((32, 32)) self.display.load_level(self.level) @@ -29,8 +29,12 @@ break else: self.display.update() + survivors = [] for m in self.level.monsters: + if m.health > 0: + survivors.append(m) self.display.animations.extend(m.act(self.level)) + self.level.monsters = survivors if self.level.hero.rect.right > self.display.width - self.margin: self.display.scroll_by(48, 0) elif self.level.hero.rect.left < self.margin: @@ -39,6 +43,7 @@ self.display.scroll_by(0, 32) elif self.level.hero.rect.top < self.margin: self.display.scroll_by(0, -32) + self.display.update_hud(self.level.hero) self.display.play_animations() pygame.display.init()
--- a/fhrl/display.py Fri Mar 15 15:57:03 2013 +0100 +++ b/fhrl/display.py Sat Mar 16 00:14:14 2013 +0100 @@ -2,6 +2,7 @@ import pygame from fhrl.resources import Resources +from fhrl.mobs import Sprite, Number class SortedUpdates(pygame.sprite.RenderUpdates): @@ -33,6 +34,7 @@ self.sprites.add(i.init_sprite(self.resources)) for s in level.stuff: self.sprites.add(s.init_sprite(self.resources)) + self.show_hud(level.hero) def _scroll_anim(self, dx, dy): frame = 0 @@ -70,8 +72,10 @@ screen.blit(self.background, rect, source) def update(self): + self.hud.clear(self.screen, self._sprite_clear) self.sprites.clear(self.screen, self._sprite_clear) dirty = self.sprites.draw(self.screen) + dirty += self.hud.draw(self.screen) pygame.display.update(dirty) self.clock.tick(24) for event in pygame.event.get(): @@ -84,6 +88,7 @@ def update_all(self): self.screen.blit(self.background, (self.x, self.y)) self.sprites.draw(self.screen) + self.hud.draw(self.screen) pygame.display.flip() def play_animations(self): @@ -100,6 +105,42 @@ if self.animations: self.update() + def show_hud(self, hero): + self.hud = SortedUpdates() + self.health_icon = Sprite() + self.health_icon.set_image(self.resources.items[10]) + self.health_icon.rect.topright = (48, 8) + self.health_number = Number(hero.health, self.resources) + self.health_number.rect.topleft = (48, 16) + self.hud.add(self.health_icon) + self.hud.add(self.health_number) + self.balls_icon = Sprite() + self.balls_icon.set_image(self.resources.items[8]) + self.balls_icon.rect.topright = (144, 8) + self.balls_number = Number(hero.balls, self.resources) + self.balls_number.rect.topleft = (144, 16) + self.hud.add(self.balls_icon) + self.hud.add(self.balls_number) + self.gasoline_icon = Sprite() + self.gasoline_icon.set_image(self.resources.items[9]) + self.gasoline_icon.rect.topright = (240, 8) + self.gasoline_number = Number(hero.gasoline, self.resources) + self.gasoline_number.rect.topleft = (240, 16) + self.hud.add(self.gasoline_icon) + self.hud.add(self.gasoline_number) + self.battery_icon = Sprite() + self.battery_icon.set_image(self.resources.items[11]) + self.battery_icon.rect.topright = (336, 8) + self.battery_number = Number(hero.battery, self.resources) + self.battery_number.rect.topleft = (336, 16) + self.hud.add(self.battery_icon) + self.hud.add(self.battery_number) + + + def update_hud(self, hero): + self.health_number.set_number(hero.health) + + def render_level(self, surf, level): for y, row in enumerate(level.grid): for x, f in enumerate(row):
--- a/fhrl/level.py Fri Mar 15 15:57:03 2013 +0100 +++ b/fhrl/level.py Sat Mar 16 00:14:14 2013 +0100 @@ -84,8 +84,9 @@ kind = random.randrange(6, 8) self.stuff.append(Stuff(kind, (x, y))) else: - kind = random.randrange(4) + kind = random.choice([0, 1, 2, 3, 8, 9, 10]) self.stuff.append(Stuff(kind, (x, y))) + self.grid[y][x] = 4 x, y = self.free.pop() self.grid[y][x] = 5
--- a/fhrl/mobs.py Fri Mar 15 15:57:03 2013 +0100 +++ b/fhrl/mobs.py Sat Mar 16 00:14:14 2013 +0100 @@ -2,6 +2,7 @@ import pygame import random +import traceback DX = [0, 1, 0, -1, -1, 1, 1, -1] @@ -9,6 +10,10 @@ XYD = {(x, y):i for i, (x, y) in enumerate(zip(DX, DY))} +def dist((x1, y1), (x2, y2)): + """Calculate the distance.""" + return abs(x1 - x2) ** 2 + abs(y1 - y2) ** 2 + class ActException(Exception): """Base class for all acting-related exceptions.""" @@ -49,6 +54,30 @@ self.rect.move_ip(pos) +class Number(pygame.sprite.DirtySprite): + def __init__(self, number, resources): + super(Number, self).__init__() + self.rect = pygame.Rect(0, 0, 1, 1) + self.digits = resources.digits + self.set_number(number) + + def set_number(self, number): + topleft = self.rect.topleft + digits = '%d' % abs(number) + self.image = pygame.Surface((12 * len(digits), 15), pygame.SRCALPHA) + for x, digit in enumerate(digits): + self.image.blit(self.digits[int(digit)], (x*12, 0)) + self.rect = self.image.get_rect() + self.rect.topleft = topleft + + def _float_anim(self, start): + self.rect.center = start + for i in xrange(8): + self.rect.move_ip(0, i - 8) + yield + self.kill() + + class Item(Sprite): def __init__(self, kind, (x, y)): super(Item, self).__init__() @@ -67,6 +96,8 @@ self.kill() + + class Stuff(Sprite): def __init__(self, kind, (x, y)): super(Stuff, self).__init__() @@ -91,16 +122,50 @@ self.rect.topleft = x * 48 - 24, y * 32 - 33 self.x, self.y = x, y self.kind = kind + self.health = kind.get('health', 1) + self.power = kind.get('power', 0) + self.ai = kind.get('ai', None) def init_sprite(self, resources): self.set_image(resources.mobs[self.kind['sprite']]) return self + def wound(self, power): + self.health -= power + + def _die_anim(self): + dy = -4 + for frame in xrange(8): + self.rect.move_ip(0, dy) + dy += 2 + yield + self.kill() + def _walk_anim(self, d): for frame in xrange(8): self.rect.move_ip(DX[d] * 6, DY[d] * 4) yield + def _attack_anim(self, d): + for frame in xrange(4): + self.rect.move_ip(DX[d] * 6, DY[d] * 4) + yield + for frame in xrange(4): + self.rect.move_ip(-DX[d] * 6, -DY[d] * 4) + yield + + def attack(self, d, mob, display=None): + anims = [] + if (self.x + DX[d], self.y + DY[d]) != (mob.x, mob.y): + raise ActException() + mob.wound(self.power) + if display: + n = Number(self.power, display.resources) + display.hud.add(n) + anims.append(n._float_anim(mob.rect.midtop)) + anims.append(self._attack_anim(d)) + return anims + def walk(self, d, level): x = self.x + DX[d] y = self.y + DY[d] @@ -113,20 +178,32 @@ raise ActBumpStuff((x, y), s) raise ActBlocked((x, y)) self.x, self.y = x, y - return self._walk_anim(d) + return [self._walk_anim(d)] def act(self, level): + if self.health <= 0: + return [self._die_anim()] + if self.ai: + try: + return self.ai(self, level) + except ActException: + traceback.print_exc() try: - return [self.walk(random.randrange(4), level)] + return self.walk(random.randrange(4), level) except ActException: return [] class Hero(Mob): def __init__(self, (x, y)): - super(Hero, self).__init__(0, (x, y)) + super(Hero, self).__init__({}, (x, y)) self.items = [] self.arrow = Sprite() + self.balls = 10 + self.gasoline = 10 + self.battery = 10 + self.health = 10 + self.power = 1 def init_sprite(self, resources): self.set_image(resources.mobs[0]) @@ -164,12 +241,14 @@ display.sprites.add(self.arrow) return anims try: - anims.append(self.walk(direction, level)) + anims.extend(self.walk(direction, level)) item = level.find_item((self.x, self.y)) if item: anims.append(item._pick_anim(display)) self.items.append(item) level.items.remove(item) + except ActBumpMob as e: + anims.extend(self.attack(direction, e.mob, display)) except ActBumpStuff as e: if e.stuff.kind in (4, 6): e.stuff.kind += 1 @@ -177,10 +256,35 @@ e.stuff.kind = 8 anims.append(e.stuff._morph_anim(display)) except ActException: - pass + return [] return anims +def ai_follow(mob, level): + possible = [ + d for d in xrange(8) + if not ( + level.is_blocked((mob.x + DX[d], mob.y + DY[d])) or + level.find_mob((mob.x + DX[d], mob.y + DY[d])) not in (None, level.hero) + ) + ] + if not possible: + return [] + random.shuffle(possible) + d = min( + possible, + key=lambda d: dist( + (level.hero.x, level.hero.y), + (mob.x + DX[d], mob.y + DY[d]) + ) + ) + try: + return mob.walk(d, level) + except ActBumpMob as e: + if e.mob == level.hero: + return mob.attack(d, e.mob) + return [] + MONSTERS = { 'guard': { @@ -188,38 +292,47 @@ 'sprite': 1, 'level': 1, 'devolve': 'worker', + 'health': 1, }, 'beast': { 'name': "Horrible Beast", 'sprite': 2, 'level': 4, 'devolve': 'fruit', + 'health': 4, + 'ai': ai_follow, + 'power': 2, }, 'robot': { 'name': "Killer Robot", 'sprite': 3, 'level': 3, 'devolve': 'scrap', + 'health': 5, }, 'professor': { 'name': "Professor Vanilla", 'sprite': 4, 'level': 0, + 'health': 10, }, 'worker': { 'name': "Construction Worker", 'sprite': 5, 'level': 2, + 'health': 3, }, 'fruit': { 'name': "Spoiled Fruit", 'sprite': 6, 'level': 0, + 'health': 1, }, 'scrap': { 'name': "Ancient Robot", 'sprite': 7, 'level': 0, + 'health': 1, }, } @@ -227,34 +340,58 @@ 'drill': { 'name': "Drill", 'sprite': 0, + 'ammo': 'gasoline', }, 'beam': { 'name': "Beam Emitter", 'sprite': 1, + 'ammo': 'battery', }, 'cannon': { 'name': "Pirate Cannon", 'sprite': 2, + 'ammo': 'balls', }, 'teleporter': { 'name': "Teleport Device", 'sprite': 3, + 'ammo': 'battery', }, 'flamer': { 'name': "Flame Thrower", 'sprite': 4, + 'ammo': 'gasoline', }, 'vulcan': { 'name': "Machine Gun", 'sprite': 5, + 'ammo': 'balls', }, 'deevo': { 'name': "Deevolution Inducer", 'sprite': 6, + 'ammo': 'battery', }, 'toaster': { 'name': "Doomsday Device", 'sprite': 7, + 'ammo': 'battery', + }, + 'balls': { + 'name': "Cannon Balls", + 'sprite': 8, + }, + 'gasoline': { + 'name': "Gasoline", + 'sprite': 9, + }, + 'medkit': { + 'name': "First Aid Kit", + 'sprite': 10, + }, + 'battery': { + 'name': "Battery", + 'sprite': 11, }, }
--- a/fhrl/resources.py Fri Mar 15 15:57:03 2013 +0100 +++ b/fhrl/resources.py Sat Mar 16 00:14:14 2013 +0100 @@ -13,6 +13,7 @@ self.stuff = self.load_atlas('stuff.png', 48, 48) self.arrows = self.load_atlas('arrows.png', 48, 48) self.items = self.load_atlas('items.png', 48, 32) + self.digits = self.load_atlas('digits.png', 12, 15) @staticmethod def load_image(filename):
