Mercurial > zday-deluxe
changeset 1:22ddffd61be9
Initial import
| author | Radomir Dopieralski <moindev@sheep.art.pl> |
|---|---|
| date | Sun, 16 Dec 2007 14:22:03 +0100 |
| parents | cec5a5dbd62c |
| children | 0b0d0e806f33 |
| files | creatures.png creatures.py data.py display.py effects.png light.py prefs.py revolver.png tiles.png util.py world.py zday.py |
| diffstat | 12 files changed, 1555 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/creatures.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,649 @@ +import time, random, sys +from data import * +from util import * +from light import Light + +def game_win(disp): + disp.clear() + disp.pause() + sys.exit() + +def game_over(disp): + disp.clear() + disp.pause() + sys.exit() + +class ActError(Exception): + pass + +class Zombie: + def __init__(self, world, pos = None): + self.world = world + if pos: + self.pos = pos + else: + x = random.randrange(1, world.width-2) + y = random.randrange(1, world.height-2) + while world.flags[x][y] & (FLAG_BLOCKED | FLAG_CREATURE): + x += 1 + if x>=world.width-1: + x = 1 + y += 1 + self.pos = (x, y) + self.target = None + self.effect = None + world.creatures.append(self) + self.world.flags[self.pos[0]][self.pos[1]] |= FLAG_CREATURE + self.burning = 0 + self.dead = 0 + self.tripped = 0 + self.hero = 0 + + def trip(self): + if not self.burning: + self.tripped = 1 + + def untrip(self): + if not self.burning: + self.tripped = 0 + + def kill(self): + x, y = self.pos + self.world.msg.write("Splat!") + self.dead = 1 + self.world.flags[x][y] &= ~FLAG_CREATURE + if not self.world.map[self.pos[0]][self.pos[1]] in (MAP_CRATE, MAP_CORPSE, MAP_WALL, MAP_DOOR, MAP_ELEV): + if self.world.flags[x][y] & FLAG_LETHAL: + self.world.add_tile(self.pos, MAP_MACHINE+1) + else: + if random.randrange(100)>20: + self.world.add_tile(self.pos, self.world.corpse) + else: + self.world.blood(self.pos, 4) + for i in range(len(self.world.creatures)): + if self.world.creatures[i]==self: + del self.world.creatures[i] + break + + def bleed(self, amount=50): + """Fills the map around the creature with blood.""" + + for dy in (-1, 0, 1): + for dx in (-1, 0, 1): + x = self.pos[0] + dx + y = self.pos[1] + dy + splatter = (random.randrange(100)+amount)/50 + self.world.blood((x,y), splatter) + + def wound(self, lethality): + if lethality>0: + self.effect = EFF_HIT + if random.randrange(100)<lethality: + self.kill() + elif lethality>50: + self.trip() + + def metabolism(self): + """Get called every turn.""" + + if self.world.flags[self.pos[0]][self.pos[1]] & FLAG_LETHAL: + self.bleed(80) + self.wound(20) + self.trip() + if self.burning: + self.effect = EFF_BURN + if random.randrange(100)>90: + self.kill() + + def burn(self): + """Called when the creature touches fire.""" + + self.burning = 1 + + def pushback(self): + """Called when the creature is pushed.""" + + hx, hy = self.world.hero.pos + x, y = self.pos + dx, dy = 0, 0 + if hx < x: + dx = 1 + elif hx > x: + dx = -1 + if hy < y: + dy = 1 + elif hy > y: + dy = -1 + try: + self.push(dx, dy) + self.trip() + except ActError: + pass + + def push(self, dx, dy): + if dx==0 and dy==0: + return + x, y = self.pos + npos = (x+dx, y+dy) + nx, ny = npos + if nx<0 or nx>=self.world.width or ny<0 or ny>=self.world.height: + raise ActError + if self.world.flags[nx][ny] & FLAG_LETHAL: + self.trip() + elif self.world.flags[nx][ny] & FLAG_BLOCKED: + raise ActError + if self.world.flags[nx][ny] & FLAG_CREATURE: + raise ActError + self.world.flags[x][y] &= ~FLAG_CREATURE + self.pos = npos + self.world.flags[nx][ny] |= FLAG_CREATURE + + def act_go(self, dx, dy): + if dx==0 and dy==0: + return + x, y = self.pos + npos = (x+dx, y+dy) + nx, ny = npos + if nx<0 or nx>=self.world.width or ny<0 or ny>=self.world.height: + raise ActError + if self.world.flags[nx][ny] & FLAG_BLOCKED: + raise ActError + if self.world.flags[nx][ny] & FLAG_CREATURE: + raise ActError + self.world.flags[x][y] &= ~FLAG_CREATURE + self.pos = npos + self.world.flags[nx][ny] |= FLAG_CREATURE + + def act_bite(self): + first = random.choice((0, 1)) + if first: + self.world.hero.defend(self) + if random.randrange(100)>10: + if distance(self.world.hero.pos, self.pos)<=1 and not self.dead: + self.world.msg.write("Chomp!") + self.world.hero.hit_bite() + if not first: + self.world.hero.defend(self) + + def act(self): + x, y = self.pos + if self.burning: + dx, dy = random.choice((-1, 0, 1)), random.choice((-1,0,1)) + for ny in range(self.pos[1]-1, self.pos[1]+1): + for nx in range(self.pos[0]-1, self.pos[0]+1): + creat = self.world.find_creature((nx, ny)) + if creat and creat!=self: + creat.burn() + try: + self.act_go(dx, dy) + except ActError: + pass + elif self.tripped: + self.untrip() + else: + if self.pos == self.target: + self.target = None + if self.world.flags[x][y] & FLAG_VISIBLE: + self.target = self.world.hero.pos + if self.target: + hx, hy = self.target + if distance(self.world.hero.pos, self.pos)<=1: + self.act_bite() + else: + dx = 0 + dy = 0 + if hx < x: + dx = -1 + elif hx > x: + dx = 1 + if hy < y: + dy = -1 + elif hy > y: + dy = 1 + try: + self.act_go(dx, dy) + except ActError: + try: + if random.choice((0, 1)): + self.act_go(dx, 0) + else: + self.act_go(0, dy) + except ActError: + pass + +class Hero(Zombie): + def __init__(self, world, disp, pos=None): + Zombie.__init__(self, world, pos) + self.disp = disp + self.disp.centermap(self.pos) + self.light = Light(world) + self.light.cast(self.pos[0], self.pos[1]) + self.inventory = Inventory(world) + self.inventory.add_item(1) + self.inventory.add_item(6) + self.inventory.add_item(12) + self.disp.inv_draw(self.inventory) + self.stamina = 100 + self.wounds = 0 + self.dead = 0 + self.food = 0 + self.aim = None + self.hero = 1 + + + def kill(self): + self.world.msg.write("Aaargh! They got me! Plant an orchid on my grave...") + game_over(self.disp) + + def wound(self, lethality=0): + self.effect = EFF_HIT + if self.stamina==0: + self.kill() + if self.wounds<5: + self.wounds+=1 + self.stamina -= random.randrange(1,10); + if self.stamina<0: + self.stamina = 0 + + def defend(self, creat): + slot = self.inventory.eqp[KIND_WEAPON] + if slot==None or self.stamina<=0: + return + weapon = items[self.inventory.inv[slot]] + try: + accuracy = weapon["accuracy"] + except KeyError: + accuracy = 100 + if random.randrange(100)<=accuracy: + self.inventory.emit_sound(slot) + try: + if items[self.inventory.inv[slot]]["noisy"]: + self.world.noise() + except KeyError: + pass + try: + creat.bleed(weapon["blood"]) + except KeyError: + pass + try: + if weapon['burn']: + creat.burn() + except KeyError: + pass + try: + if items[self.inventory.inv[slot]]["push"]: + creat.pushback() + except KeyError: + pass + try: + lethal = weapon["lethal"] + except KeyError: + lethal = 40 + creat.wound(lethal) + else: + creat.effect = EFF_MISS + self.inventory.useup_item(slot) + if random.randrange(100)<15: + self.world.msg.write("Broken.") + self.inventory.del_item(slot) + self.disp.inv_draw(self.inventory) + + def metabolism(self): + if self.food>0: + amount = min(random.randrange(3,6), self.food) + self.food -= amount + self.stamina += amount + self.stamina -= self.wounds; + if self.stamina<0: + self.stamina = 0 + if self.stamina>100: + self.stamina = 100 + + def hit_bite(self): + if random.randrange(100)>10: + self.world.msg.write("Ouch!") + self.bleed(20) + self.wound() + + def burn(self): + self.stamina -= random.randrange(10, 40) + self.world.msg.write("Burns!") + if self.stamina<0: + self.stamina = 0 + + def act_shoot(self): + if not self.target: + self.world.msg.write("Nobody here.") + raise ActError + gun_slot = self.inventory.eqp[KIND_GUN] + if gun_slot==None: + self.world.msg.write("Click!") + raise ActError + gun = items[self.inventory.inv[gun_slot]] + creat = self.world.find_creature(self.target) + if creat: + try: + accuracy = gun["accuracy"] + except KeyError: + accuracy = 100 + self.inventory.emit_sound(gun_slot) + try: + if items[self.inventory.inv[gun_slot]]["noisy"]: + self.world.noise() + except KeyError: + pass + if random.randrange(100)<=accuracy: + try: + creat.bleed(gun["blood"]) + except KeyError: + pass + try: + if gun['burn']: + creat.burn() + except KeyError: + pass + try: + if gun["push"]: + creat.pushback() + except KeyError: + pass + try: + lethal = gun["lethal"] + except KeyError: + lethal = 30 + creat.wound(lethal) + else: + self.world.msg.write("Spak!") + creat.effect = EFF_MISS + self.inventory.useup_item(gun_slot) + self.disp.inv_draw(self.inventory) + else: + self.world.msg.write("Sneaky bastard!") + raise ActError + + def act_use(self, slot): + if self.inventory.inv[slot]==0: + self.world.msg.write("It's not there.") + raise ActError + item = items[self.inventory.inv[slot]] + kind = item["kind"] + if kind in (KIND_WEAPON, KIND_GUN): + if self.inventory.eqp[kind]==slot: + self.inventory.eqp[kind] = None + else: + self.inventory.eqp[kind] = slot + self.disp.inv_draw(self.inventory) + elif kind == KIND_FOOD: + self.act_apply(slot) + else: + self.world.msg.write("Useless.") + raise ActError + + def act_trash(self, slot): + if self.inventory.inv[slot]==0: + self.world.msg.write("Gone already.") + raise ActError + self.inventory.del_item(slot) + self.world.msg.write("Useless crap!") + self.disp.inv_draw(self.inventory) + + def act_loot(self): + x, y = self.pos + if not self.world.flags[x][y] & FLAG_ITEM: + self.world.msg.write("There's nothing useful here.") + raise ActError + if self.world.map[x][y] == MAP_CRATE: + self.world.msg.write("Gently opening the crate...") + item = self.inventory.search_crate() + elif self.world.map[x][y] == MAP_CORPSE: + self.world.msg.write("Searching the corpse....") + item = self.inventory.search_corpse() + self.world.change(self.pos) + if item: + self.world.msg.write("Found something.") + self.disp.inv_draw(self.inventory) + + def act_go(self, dx, dy): + x, y = self.pos + npos = (x+dx, y+dy) + nx, ny = npos + if nx<0 or nx>self.world.width or ny<0 or ny>self.world.height: + raise ActError + if self.world.map[nx][ny] == MAP_EXIT: + self.world.msg.write("Finally made it to safety!") + game_win(self.disp) + if self.world.flags[nx][ny] & FLAG_LETHAL: + self.world.msg.write("Careful! Those gears can shred you into pieces.") + raise ActError + if self.world.flags[nx][ny] & FLAG_BLOCKED: + self.world.msg.write("Bonk!") + raise ActError + if self.world.flags[nx][ny] & FLAG_CREATURE: + creat = self.world.find_creature((nx, ny)) + if not creat: + self.world.flags[nx][ny] &= ~FLAG_CREATURE + else: + self.world.msg.write("Pardon me.") + creat.push(dx, dy) + creat.trip() + self.world.flags[x][y] &= ~FLAG_CREATURE + self.pos = npos + self.world.flags[nx][ny] |= FLAG_CREATURE + + def act_apply(self, slot): + food = items[self.inventory.inv[slot]] + try: + self.wounds -= food["heal"] + if self.wounds<0: + self.wounds = 0 + except KeyError: + pass + try: + self.food += food["food"] + if self.food>200: + self.food = 200 + except KeyError: + pass + self.inventory.emit_sound(slot) + try: + if items[self.inventory.inv[slot]]["noisy"]: + self.world.noise() + except KeyError: + pass + self.inventory.useup_item(slot) + self.disp.inv_draw(self.inventory) + + def act_run(self, dx, dy): + self.act_go(dx, dy) + if self.stamina>0: + try: + self.act_go(dx, dy) + self.stamina -= 3 + if self.stamina<0: + self.stamina = 0 + except ActError: + pass + else: + self.world.msg.write('Pant, pant...') + + def act_aim(self): + if self.aim==None: + self.aim = 0 + start = self.aim + self.world.msg.write("Changed your mind, eh?") + while 1: + self.aim+=1 + if self.aim>=len(self.world.creatures): + self.aim = 0 + x, y = self.world.creatures[self.aim].pos + if self.world.flags[x][y] & FLAG_VISIBLE and self.world.creatures[self.aim]!=self: + self.world.msg.write("Here.") + self.target = (x,y) + break + if self.aim == start: + self.world.msg.write("No more targets.") + self.aim = None + break + + def act_operate(self): + x, y = self.pos; + if self.world.map[x][y] == MAP_ELEV: + self.world.new_level() + self.disp.world_draw(self.world) + + def act(self): + x,y = self.pos + running = 0 + done = 0 +# self.target = None + + + if self.target and (not self.world.flags[self.target[0]][self.target[1]] & FLAG_CREATURE or + not self.world.flags[self.target[0]][self.target[1]] & FLAG_VISIBLE or + self.target==self.pos): + self.target=None + if self.target==None: + for creat in self.world.creatures: + if self!=creat and self.world.flags[creat.pos[0]][creat.pos[1]] & FLAG_VISIBLE and not creat.burning: + if not self.target or distance(self.pos, creat.pos) < distance(self.pos, self.target): + self.target = creat.pos + self.disp.light_draw(self.light) + self.disp.draw_stats(self) + while not done: + done = 1 + cmd = None + for creat in self.world.creatures: + self.disp.effect_draw(creat) + creat.effect = None + self.disp.msg_draw(self.world.msg) + self.disp.select(None) + self.disp.refresh() + cmd, param = self.disp.get_command(animate=1) + for creat in self.world.creatures: + self.disp.creat_draw(creat) + self.disp.select(self.target) + while not cmd: + self.disp.msg_draw(self.world.msg) + self.disp.refresh() + cmd, param = self.disp.get_command() + try: + if cmd == 'quit': + sys.exit() + elif cmd == 'refresh': + done = 0 + elif cmd == 'wait': + self.world.msg.write("Yawn...") + elif cmd == 'loot': + self.act_loot() + elif cmd == 'use': + self.act_use(param) + elif cmd == 'shoot': + self.act_shoot() + elif cmd == 'trash': + self.act_trash(param) + elif cmd == 'aim': + self.act_aim() + done = 0 + elif cmd == 'operate': + self.act_operate() + elif cmd == 'run' and param==None: + done = 0 + running = 1 + self.world.msg.write("Where to run?") + elif cmd=='go': + if running: + running = 0 + self.act_run(param[0], param[1]) + else: + self.act_go(param[0], param[1]) + except ActError: + done = 0 + self.disp.scroll_map(self.pos, self.world) + x,y = self.pos + self.light.cast(x, y) + self.world.msg.clear() + +class Inventory: + def __init__(self, world): + self.max_inv = 21 + self.inv = [] + self.world = world + self.charges = [] + self.eqp = [0, 1, None] + for i in range(self.max_inv): + self.inv.append(0) + self.charges.append(0) + self.crate_maxprob = 0 + for item in items: + try: + self.crate_maxprob += item["crate_prob"] + except KeyError: + pass + self.corpse_maxprob = 0 + for item in items: + try: + self.corpse_maxprob += item["corpse_prob"] + except KeyError: + pass + + + def add_item(self, item): + for i in range(self.max_inv): + if self.inv[i]==0: + break + if self.inv[i]: + self.world.msg.write("No more room.") + raise ActError + self.inv[i] = item + try: + self.charges[i] = items[self.inv[i]]['charges'] + except KeyError: + pass + + def del_item(self, item): + self.inv[item] = 0 + self.charges[item] = 0 + for i in range(len(self.eqp)): + if self.eqp[i]==item: + self.eqp[i] = None + + def useup_item(self, slot): + if self.charges[slot]>0: + self.charges[slot] -= 1 + if self.charges[slot]<=0: + self.world.msg.write("Empty.") + self.del_item(slot) + elif items[self.inv[slot]]["kind"]!=KIND_WEAPON: + self.del_item(slot) + + def search_crate(self): + total = 0 + spot = random.randrange(self.crate_maxprob) + for i in range(len(items)): + try: + total += items[i]['crate_prob'] + except KeyError: + pass + if total>spot: + self.add_item(i) + return i + return 0 + + def search_corpse(self): + total = 0 + spot = random.randrange(self.corpse_maxprob) + for i in range(len(items)): + try: + total += items[i]['corpse_prob'] + except KeyError: + pass + if total>spot: + self.add_item(i) + return i + return 0 + + def emit_sound(self, slot): +# try: +# self.world.msg.write(items[self.inv[slot]]["sound"]) +# except KeyError: + pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,178 @@ +# --- World data ------------------- + +EFF_HIT = 0 +EFF_BURN = 1 +EFF_MISS = 2 + +def d(**kw): + return kw +# The order must match the order in the tiles.png image file. +tiles = ( + d(name="floor", shadow=1, change=4, blood="stain"), + d(name='stain', change=0, blood="blood"), + d(name='blood', change=1, blood="flesh"), + d(name='flesh', change=2), + d(name='corpse', item=1, change=3), + d(name='rubble', change=0, blood="stain"), + d(name='wall', block=1, opaque=1, change=7, blood="bloody wall"), + d(name='crack', block=1, opaque=1, change=5, blood="bloody wall"), + d(name='bloody wall', block=1, opaque=1, change=6), + d(name='cart', item=1, change=0), + d(name='machine', block=1, opaque=1, change=11, lethal=20, blood="bloody machine"), + d(name='bloody machine', block=1, opaque=1, change=10, lethal=20), + d(name='down', operate=1), + d(name='exit', block=1, opaque=1), + d(name='up'), +) + +# XXX Obsolete +MAP_FLOOR = 0 +MAP_WALL = 6 +MAP_CORPSE = 4 +MAP_BLOOD = 1 +MAP_CRATE = 9 +MAP_ELEV = 12 +MAP_MACHINE = 10 +MAP_EXIT = 14 +MAP_DOOR = 12 + +FLAG_EXPLORED = 1 +FLAG_VISIBLE = 2 +FLAG_CREATURE = 4 +FLAG_ITEM = 8 +FLAG_BLOCKED = 16 +FLAG_OPAQUE = 32 +FLAG_LETHAL = 64 +FLAG_NONE = 0 + +KIND_WEAPON = 0 +KIND_GUN = 1 + +KIND_AMMO = 2 +KIND_FOOD = 3 +KIND_TRASH = 4 + +items = ( + { "name": '---', + "kind": KIND_TRASH, + "corpse_prob": 8, + }, + { "name": 'umbrella', + "kind": KIND_WEAPON, + "crate_prob": 2, + "corpse_prob": 6, + "accuracy": 70, + "sound": "Whack", + "blood": 20, + "lethal": 30, + }, + { "name": 'axe', + "kind": KIND_WEAPON, + "crate_prob": 8, + "accuracy": 85, + "sound": "Hack", + "push": 1, + "blood": 60, + "lethal": 50, + }, + { "name": 'blowtorch', + "kind": KIND_WEAPON, + "charges": 3, + "crate_prob": 4, + "accuracy": 75, + "lethal": 0, + "burn": 1, + "sound": "Sslsh", + }, + { "name": 'chainsaw', + "kind": KIND_WEAPON, + "crate_prob": 2, + "charges": 6, + "sound": "Wrrrooaaam!", + "noisy": 1, + "push": 1, + "blood": 200, + "lethal": 100, + }, + { "name": 'revolver', + "kind": KIND_GUN, + "corpse_prob": 6, + "charges": 6, + "accuracy": 95, + "sound": "Bang!", + "noisy": 1, + "lethal": 60, + "blood": 40, + }, + { "name": 'nail gun', + "kind": KIND_GUN, + "crate_prob": 8, + "charges": 24, + "accuracy": 70, + "sound": "Zing!", + "blood": 20, + "lethal": 40, + }, + { "name": 'shotgun', + "kind": KIND_GUN, + "corpse_prob": 4, + "charges": 8, + "sound": "Blam!", + "push": 1, + "noisy": 1, + "lethal": 70, + "blood": 80, + }, + { "name": 'molotov', + "kind": KIND_GUN, + "corpse_prob": 15, + "accuracy": 90, + "lethal": 10, + "burn": 1, + "sound": "Swoosh!", + }, + { "name": 'chocolates', + "kind": KIND_FOOD, + "corpse_prob": 12, + "charges": 6, + "food": 10, + "sound": "Yummy, my favorite.", + }, + { "name": 'ration', + "kind": KIND_FOOD, + "corpse_prob": 12, + "food": 40, + "sound": "Munch, munch!", + }, + { "name": 'med pack', + "kind": KIND_FOOD, + "crate_prob": 4, + "corpse_prob": 2, + "heal": 3, + "sound": "Much beter!", + }, + { "name": 'wound spray', + "kind": KIND_FOOD, + "corpse_prob": 5, + "heal": 1, + "charges": 3, + "sound": "Psshh!", + }, + { "name": 'can of spam', + "kind": KIND_FOOD, + "corpse_prob": 8, + "food": 60, + "sound": "Lovely spam.", + }, + { "name": 'teddy bear', + "kind": KIND_WEAPON, + "lethal": 0, + "corpse_prob": 4, + "push": 1, + "sound": "Hug me!", + }, +) + + +# --- End of world data ------------ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/display.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,210 @@ +#!/usr/bin/python +import pygame +from pygame.locals import * +import sys + +from prefs import * +from data import * + + +class DisplayPygame: + def __init__(self): + pygame.init() + self.screen = pygame.display.set_mode((800, 600)) + pygame.key.set_repeat(500, 30) + self.font = pygame.font.SysFont("DejaVu Sans Mono, Bitstream Vera Sans Mono, Lucida Console, Lucida Typewriter, monospace", 16, bold=True, italic=False) + self.maxmapx = 39 + self.maxmapy = 35 + self.msgh = 32 + self.xtile = 16 + self.ytile = 16 + self.dirty =[] + self.empty = pygame.Surface((self.xtile,self.ytile)) + self.empty.fill((0x0f, 0x0f, 0x0f)) + self.fog = pygame.Surface((self.xtile,self.ytile)) + self.fog.fill((0x44, 0x00, 0x44)) + self.fog.set_alpha(90, RLEACCEL) + self.cursor = pygame.Surface((self.xtile,self.ytile)) + self.cursor.fill((0x00, 0x66, 0x00)) + self.cursor.set_alpha(90, RLEACCEL) + self.screen.fill((0x0f, 0x0f, 0x0f)) + pygame.display.update() + + self.creatures = self._read_frames("creatures.png") + self.effects = self._read_frames("effects.png") + self.tiles = self._read_frames("tiles.png") + self.cursor = self.effects[1][0] + + def _read_frames(self, filename, size=16): + '''Read an image and split it into tiles.''' + + image = pygame.image.load(filename).convert() + w, h = image.get_size() + frames = [] + for x in range(0, w/size): + frames.append([]) + for y in range(0, h/size): + frames[x].append(image.subsurface((x*size, y*size, size, size))) + return frames + + + def pause(self): + cmd = None + self.refresh() + while cmd!="more": + cmd, parm = self.get_command() + + def clear(self): + self.screen.fill((0x0f, 0x0f, 0x0f)) + + def _blit_map(self, x, y, img, clear=1): + ax, ay = x - self.xmap, y - self.ymap + if (ax<0) or (ax>=self.maxmapx) or (ay<0) or (ay>=self.maxmapy): + return + r = Rect(self.xtile*ax, self.msgh+self.ytile*ay, self.xtile, self.ytile) + if clear: + self.screen.fill((0x00, 0x00, 0x00), r) + self.screen.blit(img, r) + self.dirty.append(r) + + def tile_draw(self, pos, world): + x, y = pos + if (x-self.xmap<0) or (x-self.xmap>=self.maxmapx) or (y-self.ymap<0) or (y-self.ymap>=self.maxmapy): + return + flags = world.flags[x][y] + maptile = world.map[x][y] + tile = tiles[maptile] + if flags & FLAG_VISIBLE or flags & FLAG_EXPLORED: + try: + img = self.tiles[maptile][0] + except IndexError: + img = self.empty + self.font.render("%d"%maptile, True, (0,0,0)).blit(img, (0,0,16,16)) + self._blit_map(x, y, img) + if not (flags & FLAG_VISIBLE): + self._blit_map(x, y, self.fog, clear=0) + else: + self._blit_map(x, y, self.empty, clear = 0) + + def world_draw(self, world): + r =Rect(0, self.msgh, self.xtile*self.maxmapx, self.ytile*self.maxmapy) + self.dirty.append(r) + self.screen.fill((0x00, 0x00, 0x00), Rect(0, self.msgh, self.xtile*self.maxmapx, self.ytile*self.maxmapy)) + for x in range(world.width): + for y in range(world.height): + self.tile_draw((x, y), world) + + def inv_draw(self, inv): + r =Rect(self.xtile*self.maxmapx, self.msgh, 800-self.xtile*self.maxmapx, self.ytile*self.maxmapy) + self.screen.fill((0x00, 0x00, 0x00), r) + for i in range(inv.max_inv): + if i in inv.eqp: + color = (0x00, 0xff, 0x00) + else: + color = (0x00, 0x66, 0x00) + if inv.charges[i]>0: + img = self.font.render("%s %s %d" % (chr(ord('a')+i), items[inv.inv[i]]['name'], inv.charges[i]), True, color) + else: + img = self.font.render("%s %s" % (chr(ord('a')+i), items[inv.inv[i]]['name']), True, color) + self.screen.blit(img, (self.xtile*self.maxmapx, self.msgh+i*self.ytile)) + self.dirty.append(r) + + def draw_stats(self, hero): + r = Rect(self.xtile*self.maxmapx, 0, 800-self.xtile*self.maxmapx, self.msgh) + self.screen.fill((0x00, 0x00, 0x00),r) + wounds = '' + for i in range(hero.wounds): + wounds += '*' + img = self.font.render("S:%3d W:%s" % (hero.stamina, wounds), True, (0xff, 0x22, 0x22), (0x00, 0x00, 0x00)) + self.screen.blit(img, r) + self.dirty.append(r) + + def refresh(self): + pygame.display.update(self.dirty) + self.dirty = [] + + def msg_draw(self, msg): + self.screen.fill((0x00, 0x00, 0x00), (0,0,self.xtile*self.maxmapx , self.msgh)) + text = "" + for m in msg.last: + text += m + " " + img = self.font.render(text, True, (0x00, 0x66, 0x00)) + self.screen.blit(img, (0, 0)) + text = "" + for m in msg.mesgs: + text += m + " " + img = self.font.render(text, True, (0x00, 0xff, 0x00)) + self.screen.blit(img, (0, 16)) + self.dirty.append(Rect(0,0, 800, self.msgh)) + + def light_draw(self, light): + for pos in light.last: + if not pos in light.lit: + self.tile_draw(pos, light.world) + for pos in light.lit: + self.tile_draw(pos, light.world) + + def creat_draw(self, creat): + x, y = creat.pos + if x-self.xmap<0 or x-self.xmap>=self.maxmapx or y-self.ymap<0 or y-self.ymap>=self.maxmapy: + return + if not creat.world.flags[x][y] & FLAG_VISIBLE: + return + if creat.hero: + img = self.creatures[0][0] + elif creat.burning: + img = self.creatures[2][0] + else: + img = self.creatures[1][0] + self._blit_map(x, y, img) + + def centermap(self, pos): + x, y = pos + self.xmap = - (self.maxmapx / 2 - x) + self.ymap = - (self.maxmapy / 2 - y) + + def scroll_map(self, pos, world): + x, y = pos + maxy, maxx = self.maxmapy, self.maxmapx + if x-self.xmap<margin: + self.xmap-=margin + self.world_draw(world) + elif y-self.ymap<margin: + self.ymap-=margin + self.world_draw(world) + elif x-self.xmap>self.maxmapx-margin: + self.xmap+=margin + self.world_draw(world) + elif y-self.ymap>self.maxmapy-margin: + self.ymap+=margin + self.world_draw(world) + + def select(self, pos): + if not pos: + return + x, y = pos + self._blit_map(x, y, self.cursor, False) + + def get_command(self, animate=0): + shift = 0 + if animate: + return (None, None) + while 1: + event=pygame.event.wait() + if event.type == QUIT: + sys.exit() + elif event.type == KEYDOWN: + name = pygame.key.name(event.key) + if len(name)==1: + mods = pygame.key.get_mods() + if mods & KMOD_LSHIFT or mods & KMOD_RSHIFT: + name = chr(ord(name)-32) # XXX A hack for uppercase :) + try: + cmd, parm = command_keys[name] + return (cmd, parm) + except KeyError: + #self.msg.write("What does %s mean?" % name) + return (None, None) + + def effect_draw(self, creat): + pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/light.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,86 @@ +import math +from data import * +from util import * + +class Light: + maxrays = 255 + maxradius = 12 + ray_max = [] + ray_min = [] + + + def __init__(self, world): + self.world = world + self.lit = [] + self.last = [] + self.radius = 12 + self.visible = set() + self.new = set() + self.explored = set() + if not self.ray_max: + for y in range(self.maxradius): + line_max = [] + line_min = [] + for x in range(self.maxradius): + line_max.append(0) + line_min.append(self.maxrays) + self.ray_max.append(line_max) + self.ray_min.append(line_min) + for ray in range(self.maxrays): + an = (math.pi*ray)/(self.maxrays*2) + s = math.sin(an)/4 + c = math.cos(an)/4 + d =0 + x =0 + y =0 + while 1: + x = int(math.floor(s*d)) + y = int(math.floor(c*d)) + d += 1 + if x<self.radius and y<self.radius: + self.ray_min[x][y] = min(self.ray_min[x][y], ray) + self.ray_max[x][y] = max(self.ray_max[x][y], ray) + else: + break + + def _quart(self, xpos, ypos, dx, dy): + rays = [] + ox = xpos + for i in range(self.maxrays): + rays.append(0) + corner = self.radius + for y in range(self.radius): + if (2*y>=self.radius): + corner -= 1 + for x in range(corner): + opaque = self.world.is_opaque((xpos,ypos)) + shadow = 0 + rayweight = self.maxrays / (self.ray_max[x][y] - self.ray_min[x][y]) + for ray in range(self.ray_min[x][y], self.ray_max[x][y]): + if rays[ray]: + shadow += rayweight + if 3*shadow<2*self.maxrays: + self.lit.append((xpos, ypos)) + self.world.flags[xpos][ypos] |= FLAG_VISIBLE + if opaque: + for ray in range(self.ray_min[x][y], self.ray_max[x][y]): + rays[ray] = 1 + xpos += dx + if xpos<0 or xpos>=self.world.width: + break + ypos += dy + if ypos<0 or ypos>=self.world.width: + break + xpos = ox + + def cast(self, xpos, ypos): + self.last = self.lit + r = self.radius + for (x,y) in self.last: + self.world.flags[x][y] &= ~FLAG_VISIBLE + self.world.flags[x][y] |= FLAG_EXPLORED + self.lit = [(xpos,ypos)] + self._quart(xpos, ypos, 1, 1) + self._quart(xpos, ypos, -1, 1) + self._quart(xpos, ypos, 1, -1) + self._quart(xpos, ypos, -1, -1)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prefs.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,98 @@ +# --- User preferences data -------- +margin = 9 +command_keys = { + "KEY_UP": ("go", ( 0,-1)), + "KEY_DOWN": ("go", ( 0, 1)), + "KEY_LEFT": ("go", (-1, 0)), + "KEY_RIGHT": ("go", ( 1, 0)), + "KEY_A1": ("go", (-1,-1)), + "KEY_A3": ("go", ( 1,-1)), + "KEY_C1": ("go", (-1, 1)), + "KEY_C3": ("go", ( 1, 1)), + "KEY_HOME": ("go", (-1,-1)), + "KEY_FIND": ("go", (-1,-1)), + "KEY_PPAGE": ("go", ( 1,-1)), + "KEY_END": ("go", (-1, 1)), + "KEY_SELECT": ("go", (-1, 1)), + "KEY_NPAGE": ("go", ( 1, 1)), + + '8': ("go", ( 0,-1)), + '2': ("go", ( 0, 1)), + '4': ("go", (-1, 0)), + '6': ("go", ( 1, 0)), + '7': ("go", (-1,-1)), + '9': ("go", ( 1,-1)), + '1': ("go", (-1, 1)), + '3': ("go", ( 1, 1)), + + 'a': ("use", 0), + 'b': ("use", 1), + 'c': ("use", 2), + 'd': ("use", 3), + 'e': ("use", 4), + 'f': ("use", 5), + 'g': ("use", 6), + 'h': ("use", 7), + 'i': ("use", 8), + 'j': ("use", 9), + 'k': ("use", 10), + 'l': ("use", 11), + 'm': ("use", 12), + 'n': ("use", 13), + 'o': ("use", 14), + 'p': ("use", 15), + 'q': ("use", 16), + 'r': ("use", 17), + 's': ("use", 18), + 't': ("use", 19), + 'u': ("use", 20), + + 'A': ("trash", 0), + 'B': ("trash", 1), + 'C': ("trash", 2), + 'D': ("trash", 3), + 'E': ("trash", 4), + 'F': ("trash", 5), + 'G': ("trash", 6), + 'H': ("trash", 7), + 'I': ("trash", 8), + 'J': ("trash", 9), + 'K': ("trash", 10), + 'L': ("trash", 11), + 'M': ("trash", 12), + 'N': ("trash", 13), + 'O': ("trash", 14), + 'P': ("trash", 15), + 'Q': ("trash", 16), + 'R': ("trash", 17), + 'S': ("trash", 18), + 'T': ("trash", 19), + 'U': ("trash", 20), + + "KEY_B2": ("wait", None), + '5': ("wait", None), + + 'v': ("run", None), + 'x': ("loot", None), + 'z': ("shoot", None), + 'Z': ("aim", None), + 'X': ("operate", None), + + '!': ("quit", None), + 'KEY_ESC': ("quit", None), + ' ': ("more", None), + +# for SDL: + 'escape': ("quit", None), + "[8]": ("go", ( 0,-1)), + "[2]": ("go", ( 0, 1)), + "[4]": ("go", (-1, 0)), + "[6]": ("go", ( 1, 0)), + "[7]": ("go", (-1,-1)), + "[9]": ("go", ( 1,-1)), + "[1]": ("go", (-1, 1)), + "[3]": ("go", ( 1, 1)), + '[5]': ("wait", None), + 'space': ("more", None), +} +# --- End of user preferences data -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,5 @@ +def distance(a, b): + x1, y1 = a + x2, y2 = b + return (max(abs(x2-x1), abs(y2-y1)) + abs(x2-x1) + abs(y2-y1))/2 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/world.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,295 @@ +import time, random +from data import * +from creatures import Zombie +from util import * + +class TileError: + """Exception rised when a named tile is not found.""" + pass + +class Mesg: + """Keep all the messages to be displayed.""" + + def __init__(self): + self.mesgs = [] + self.last = [] + self.history = [] + + def clear(self): + self.last = self.mesgs + self.history += self.mesgs + self.mesgs = [] + + def write(self, msg): + self.mesgs.append(msg) + + def output(self): + """Output the messages to stdout, for debugging.""" + + for msg in self.last: + print msg + for msg in self.mesgs: + print msg + +class World: + """Hold all the information about the current state of + the game, the map and the creatures.""" + + def __init__(self): + self.width, self.height = 64, 64 + self.hero = None + self.msg = Mesg() + self.level = 0 + # The basic tile names + self.wall = self.named_tile("wall") + self.floor = self.named_tile("floor") + self.rubble = self.named_tile("rubble") + self.cart = self.named_tile("cart") + self.corpse = self.named_tile("corpse") + self.machine = self.named_tile("machine") + # Generate the first level + self.new_level() + + def new_level(self): + """Discard the old map and create a new one.""" + + self.level += 1 + self.map = [] + self.flags = [] + for x in xrange(self.width): + line_map = [] + line_flags = [] + for y in xrange(self.height): + line_map.append(self.floor) + line_flags.append(FLAG_NONE) + self.map.append(line_map) + self.flags.append(line_flags) + if self.level<5: + self.generate() + self.decorate() + self.erode() + else: + self.final() + if self.hero: + self.creatures = [self.hero] + else: + self.creatures = [] + self.populate() + + def find_creature(self, pos): + """Return the creature that stands on the specified + position, or None if the position is unoccupied.""" + + for creat in self.creatures: + if creat.pos == pos: + return creat + return None + + def named_tile(self, name): + """Returns the number of the tile that has specified name. + If no tile with this name exists, throws an exception.""" + for tile, i in zip(tiles, xrange(100)): + if tile["name"]==name: + return i + raise TileError + + def add_tile(self, pos, tile): + """Puts the specified tile in the specified position + of the map.""" + + x,y = pos + self.map[x][y] = tile + info = tiles[tile] + if 'block' in info: + self.flags[x][y] |= FLAG_BLOCKED + else: + self.flags[x][y] &= ~FLAG_BLOCKED + if 'opaque' in info: + self.flags[x][y] |= FLAG_OPAQUE + else: + self.flags[x][y] &= ~FLAG_OPAQUE + if 'item' in info: + self.flags[x][y] |= FLAG_ITEM + else: + self.flags[x][y] &= ~FLAG_ITEM + if 'lethal' in info: + self.flags[x][y] |= FLAG_LETHAL + else: + self.flags[x][y] &= ~FLAG_LETHAL + + def add_rect(self, topleft, bottomright, tile): + """Draws a rectange with specified tile kind.""" + + x1, y1 = topleft + x2, y2 = bottomright + for y in xrange(y1, y2): + self.add_tile((x1, y), tile) + self.add_tile((x2, y), tile) + for x in xrange(x1, x2): + self.add_tile((x, y1), tile) + self.add_tile((x, y2), tile) + + def fill_rect(self, topleft, bottomright, tile): + """Draws a fileld rectangle on the map.""" + + x1, y1 = topleft + x2, y2 = bottomright + for y in xrange(y1, y2): + for x in xrange(x1, x2): + self.add_tile((x, y), tile) + + def decorate(self): + """Scatters various "decorations" around the map.""" + + for i in xrange(random.randrange(5, 16)): + x = random.randrange(1, self.width-2) + y = random.randrange(1, self.width-2) + self.add_tile((x, y), self.corpse) + for i in xrange(random.randrange(25, 40)): + x = random.randrange(1, self.width-2) + y = random.randrange(1, self.width-2) + self.add_tile((x, y), self.cart) + for i in xrange(5): + while 1: # XXX this assumes there's always at least one floor tile! + x = random.randrange(1, self.width-2) + y = random.randrange(1, self.width-2) + if self.map[x][y] == self.floor: + self.add_tile((x, y), self.named_tile("down")) + break + + + def erode(self): + """Changes some tiles of the map into a more "destroyed" kind.""" + + for i in xrange(random.randrange(350, 700)): + x = random.randrange(1, self.width-2) + y = random.randrange(1, self.width-2) + self.change((x, y)) + + def populate(self): + """Fills the map with zombies and the hero.""" + + if self.hero: + x, y = self.hero.pos + self.add_tile((x, y), self.named_tile("up")) + self.flags[x][y] |= FLAG_CREATURE + for i in xrange(random.randrange(50+5*self.level, 90+20*self.level)): + Zombie(self) + + + def put_doors(self, pos1, pos2, tile): + """Puts specified tile in a one, random wall of the + specified rectangle.""" + + x1,y1= pos1 + x2,y2=pos2 + if random.choice((0,1)): + if x2-x1>2: + x = random.randrange(x1+1, x2-1) + if random.choice((0,1)): + y = y1 + else: + y = y2 + self.add_tile((x, y), tile) + else: + if y2-y1>2: + y = random.randrange(y1+1, y2-1) + if random.choice((0,1)): + x = x1 + else: + x = x2 + self.add_tile((x, y), tile) + + def final(self): + """Create the final level.""" + + for i in range(10): + x1 = random.randrange(1, self.width-2) + y1 = random.randrange(1, self.height-2) + x2 = min(random.randrange(x1+1, self.width-1), x1+10) + y2 = min(random.randrange(y1+1, self.height-1), y1+10) + self.add_rect((x1+1,y1+1), (x2-1,y2-1), self.floor) + self.add_rect((x1,y1), (x2,y2), self.wall) + self.add_rect((x1-1,y1-1), (x2+1,y2+1), self.floor) + for j in range(random.randrange(4, 8)): + self.put_doors((x1,y1),(x2,y2), self.floor) + for x in range(self.width): + self.add_tile((x, 0), self.wall) + self.add_tile((x, self.height-1), self.wall) + for y in range(self.width): + self.add_tile((0, y), self.wall) + self.add_tile((self.width-1, y), self.wall) + for j in range(random.randrange(8, 15)): + self.put_doors((0,0),(self.width,self.height), self.named_tile("exit")) + + def generate(self): + """Create a new level.""" + + for i in range(130): + x1 = random.randrange(1, self.width-2) + y1 = random.randrange(1, self.height-2) + x2 = min(random.randrange(x1+1, self.width-1), x1+10) + y2 = min(random.randrange(y1+1, self.height-1), y1+10) + self.add_rect((x1+1,y1+1), (x2-1,y2-1), self.floor) + self.add_rect((x1,y1), (x2,y2), self.wall) + self.add_rect((x1-1,y1-1), (x2+1,y2+1), self.floor) + for j in range(random.randrange(2, 4)): + self.put_doors((x1,y1),(x2,y2), self.floor) + for j in range(random.randrange(4)): + self.put_doors((x1,y1),(x2,y2), self.rubble) + for j in range(random.randrange(4)): + self.put_doors((x1,y1),(x2,y2), self.machine) + for i in range(20): + x1 = random.randrange(1, self.width-2) + y1 = random.randrange(1, self.height-2) + x2 = min(random.randrange(x1+1, self.width-1), x1+5) + y2 = min(random.randrange(y1+1, self.height-1), y1+5) + self.fill_rect((x1,y1), (x2,y2), self.floor) + for x in range(self.width): + self.add_tile((x, 0), self.wall) + self.add_tile((x, self.height-1), self.wall) + for y in range(self.width): + self.add_tile((0, y), self.wall) + self.add_tile((self.width-1, y), self.wall) + + def change(self, pos): + """Changes the tile at specified position into a more + destroyed version.""" + + x, y = pos + changed = tiles[self.map[x][y]].get("change", None) + if changed: + self.add_tile(pos, changed) + + def blood(self, pos, amount=1): + """Changes the tile at specified position into a more + bloody version. Optional amount adds more blood.""" + + x, y = pos + t = self.map[x][y] + if t==self.wall and amount<3: + return + for i in xrange(amount): + changed = tiles[t].get("blood", None) + if changed: + self.add_tile(pos, self.named_tile(changed)) + + def noise(self): + """Make all the creature close to the hero aware of him.""" + + for creat in self.creatures: + if distance(creat.pos, self.hero.pos)<10: + if not creat.target: + creat.target = self.hero.pos + + def is_opaque(self, pos): + """Return true if the tile at the specified position blocks light.""" + + x, y = pos + return bool(tiles[self.map[x][y]].get("opaque", False)) + + def is_blocked(self, pos): + """Return true if the tile at the specified postion blocks movement.""" + + x,y = pos + return bool(tiles[self.map[x][y]].get("block", False))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/zday.py Sun Dec 16 14:22:03 2007 +0100 @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +#------------------------------------------ +# @@@@@@@@ @@@@@@@ @@@@ @@@ @@@ +# YYYYYYY YYYYYYYY YYY YYY YYY YYY +# TTT TT TT TTT TT TTT TTTT +# !! !! !! !!!!!!! !! +# :::::: ::::: : : : +#------------------------------------------ + +# Licence: +# +# You're permitted do do anything with this code, provided +# you contact the author and get his permission. +# You can contact the author at sheep@sheep.prv.pl +# +# Copyright 2005 by Radomir 'The Sheep' Dopieralski. + +from display import DisplayPygame +from world import World +from creatures import Hero + +def main(): + disp = DisplayPygame() + world = World() + world.hero = Hero(world, disp) + disp.world_draw(world) + while 1: + for creat in world.creatures: + creat.metabolism() + if not creat.dead: + creat.act() + +main()
