Mercurial > zday
changeset 0:0de4cbe2e6a1 default tip
initial commit
| author | Radomir Dopieralski <sheep-devel@sheep.art.pl> |
|---|---|
| date | Sat, 13 Oct 2012 20:54:24 +0200 |
| parents | |
| children | |
| files | zday.py |
| diffstat | 1 files changed, 1878 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/zday.py Sat Oct 13 20:54:24 2012 +0200 @@ -0,0 +1,1878 @@ +#!/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. + +import sys, time, random, math + +# --- 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 - + +# --- World data ------------------- + +C_GRAY = 0 +C_WHITE = 1 +C_BLACK = 2 +C_LIME = 3 +C_GREEN = 4 +C_YELLOW = 5 +C_BROWN = 6 +C_LRED = 7 +C_RED = 8 +C_AQUA = 9 +C_CYAN = 10 +C_MAGENTA = 11 + +effects = ( + { + "char": '*', + "color": C_LRED, + }, { + "char": '+', + "color": C_WHITE, + }, { + "char": 'x', + "color": C_YELLOW, + }, +) + +EFF_HIT = 0 +EFF_BURN = 1 +EFF_MISS = 2 + +tiles = ( + { + "name": 'floor', + "char": '.', + "color": C_GRAY, + "shadow": 1, + "change": 4, + }, { + "name": 'wall', + "char": '#', + "color": C_GRAY, + "block": 1, + "opaque": 1, + "change": 5, + }, { + "name": 'corpse', + "char": '%', + "color": C_LRED, + "item": 1, + "change": 3, + "highlight":1, + }, { + "name": 'pile of flesh', + "char": '+', + "color": C_RED, + }, { + "name": 'pile of rubble', + "char": ':', + "color": C_BLACK, + "change": 0, + }, { + "name": 'ruined wall', + "char": '#', + "color": C_BLACK, + "block": 1, + "opaque": 1, + "change": 4, + }, { + "name": 'blood pool', + "char": ':', + "color": C_RED, + "change": 0, + }, { + "name": 'blood stain', + "char": '.', + "color": C_RED, + "change": 0, + }, { + "name": 'bloody wall', + "char": '#', + "color": C_LRED, + "block": 1, + "opaque": 1, + "change": 5, + }, { + "name": 'crate', + "char": '&', + "color": C_YELLOW, + "item": 1, + "change": 4, + "highlight":1, + }, { + "name": 'escalator down', + "char": '>', + "color": C_AQUA, + "operate": 1, + "highlight":1, + }, { + "name": 'escalator up', + "char": '<', + "color": C_CYAN, + "operate": 1, + }, { + "name": 'closed door', + "char": '+', + "color": C_BROWN, + "block": 1, + "opaque": 1, + "change": 13, + "operate": 1, + }, { + "name": 'open door', + "char": "'", + "color": C_BROWN, + "change": 12, + "operate": 1, + }, { + "name": 'machinery', + "char": "$", + "color": C_CYAN, + "block": 1, + "opaque": 1, + "change": 5, + "highlight":1, + "lethal": 20, + }, { + "name": 'bloody machinery', + "char": "$", + "color": C_LRED, + "block": 1, + "opaque": 1, + "change": 5, + "highlight":1, + "lethal": 20, + }, { + "name": 'exit', + "char": "X", + "color": C_LIME, + "block": 1, + "opaque": 1, + "highlight":1, + }, +) + +hero_char = '@' +hero_color = C_WHITE +zombie_char = 'Z' +zombie_color = C_GREEN +burning_char = '*' +burning_color = C_YELLOW + +MAP_FLOOR = 0 +MAP_WALL = 1 +MAP_CORPSE = 2 +MAP_BLOOD = 6 +MAP_CRATE = 9 +MAP_ELEV = 10 +MAP_DOOR = 12 +MAP_MACHINE = 14 +MAP_EXIT = 16 + +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 ------------ + + +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 + + +def game_win(disp): + disp.clear() + disp.pause() + sys.exit() + +def game_over(disp): + disp.clear() + disp.pause() + sys.exit() + + +class Mesg: + 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): + for msg in self.last: + print msg + for msg in self.mesgs: + print msg + +class Light: + maxrays = 255 + maxradius = 12 + ray_max = [] + ray_min = [] + def __init__(self, world): + self.world = world + self.lit = [] + self.last = [] + self.radius = 12 + 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.flags[xpos][ypos] & FLAG_OPAQUE + 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) + +class World: + def __init__(self): + self.width, self.height = 64, 64 + self.hero = None + self.level = 0 + self.new_level() + + def new_level(self): + self.level+=1 + self.map = [] + self.flags = [] + for x in range(self.width): + line_map = [] + line_flags = [] + for y in range(self.height): + line_map.append(MAP_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): + for creat in self.creatures: + if creat.pos == pos: + return creat + return None + + def add_tile(self, pos, tile): + x,y = pos + if x<0 or y<0 or x>=self.width or y>=self.height: + return + self.map[x][y] = tile + try: + tmp = tiles[tile]['block'] + self.flags[x][y] |= FLAG_BLOCKED + except KeyError: + self.flags[x][y] &= ~FLAG_BLOCKED + try: + tmp = tiles[tile]['opaque'] + self.flags[x][y] |= FLAG_OPAQUE + except KeyError: + self.flags[x][y] &= ~FLAG_OPAQUE + try: + tmp = tiles[tile]['item'] + self.flags[x][y] |= FLAG_ITEM + except KeyError: + self.flags[x][y] &= ~FLAG_ITEM + try: + tmp = tiles[tile]['lethal'] + self.flags[x][y] |= FLAG_LETHAL + except KeyError: + self.flags[x][y] &= ~FLAG_LETHAL + + def add_rect(self, topleft, bottomright, tile): + x1, y1 = topleft + x2, y2 = bottomright + for y in range(y1, y2): + self.add_tile((x1, y), tile) + self.add_tile((x2, y), tile) + for x in range(x1, x2): + self.add_tile((x, y1), tile) + self.add_tile((x, y2), tile) + + def fill_rect(self, topleft, bottomright, tile): + x1, y1 = topleft + x2, y2 = bottomright + for y in range(y1, y2): + for x in range(x1, x2): + self.add_tile((x, y), tile) + + def decorate(self): + for i in range(random.randrange(5, 20)): + x = random.randrange(1, self.width-2) + y = random.randrange(1, self.width-2) + self.add_tile((x, y), MAP_CORPSE) + for i in range(random.randrange(25, 40)): + x = random.randrange(1, self.width-2) + y = random.randrange(1, self.width-2) + self.add_tile((x, y), MAP_CRATE) + for i in range(5): + while 1: + x = random.randrange(1, self.width-2) + y = random.randrange(1, self.width-2) + if self.map[x][y] == MAP_FLOOR: + self.add_tile((x, y), MAP_ELEV) + break + + + def erode(self): + for i in range(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): + if self.hero: + x,y = self.hero.pos + self.add_tile((x, y), MAP_ELEV + 1) + self.flags[x][y] |= FLAG_CREATURE + for i in range(random.randrange(50+5*self.level, 90+20*self.level)): + Zombie(self) + + + def put_doors(self, pos1, pos2, tile): + 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): + 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), MAP_FLOOR) + self.add_rect((x1,y1), (x2,y2), MAP_WALL) + self.add_rect((x1-1,y1-1), (x2+1,y2+1), MAP_FLOOR) + for j in range(random.randrange(4, 8)): + self.put_doors((x1,y1),(x2,y2), MAP_FLOOR) + for x in range(self.width): + self.add_tile((x, 0), MAP_WALL) + self.add_tile((x, self.height-1), MAP_WALL) + for y in range(self.width): + self.add_tile((0, y), MAP_WALL) + self.add_tile((self.width-1, y), MAP_WALL) + for j in range(random.randrange(8, 15)): + self.put_doors((0,0),(self.width,self.height), MAP_EXIT) + + def generate(self): + 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), MAP_FLOOR) + self.add_rect((x1,y1), (x2,y2), MAP_WALL) + self.add_rect((x1-1,y1-1), (x2+1,y2+1), MAP_FLOOR) + for j in range(random.randrange(2, 4)): + self.put_doors((x1,y1),(x2,y2), MAP_FLOOR) + for j in range(random.randrange(4)): + self.put_doors((x1,y1),(x2,y2), MAP_FLOOR+1) + for j in range(random.randrange(4)): + self.put_doors((x1,y1),(x2,y2), MAP_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), MAP_FLOOR) + for x in range(self.width): + self.add_tile((x, 0), MAP_WALL) + self.add_tile((x, self.height-1), MAP_WALL) + for y in range(self.width): + self.add_tile((0, y), MAP_WALL) + self.add_tile((self.width-1, y), MAP_WALL) + + def change(self, pos): + x, y = pos + try: + changed = tiles[self.map[x][y]]["change"] + except KeyError: + return + self.add_tile(pos, changed) + + def noise(self): + for creat in self.creatures: + if distance(creat.pos, self.hero.pos)<10: + if not creat.target: + creat.target = self.hero.pos + + +class Display: + def __init__(self): + pass + + def clear(self): + pass + + def msg_draw(self, msg): + pass + + def creat_draw(self, creat): + pass + + def effect_draw(self, creat): + pass + + def tile_draw(self, pos, world): + pass + + def light_draw(self, light): + pass + + def inv_draw(self, inv): + pass + + def draw_stats(self, hero): + pass + + def world_draw(self, world): + pass + + def select(self, pos): + pass + + def centermap(self, pos): + pass + + def refresh(self): + pass + + def scroll_map(self, pos, world): + pass + + def get_command(self, animate=0): + return (None, None) + + def pause(self): + cmd = None + msg.write("More...") + self.msg_draw(msg) + self.refresh() + while cmd!="more": + cmd, parm = self.get_command() + +class DisplayPygame(Display): + def __init__(self): + self.screen = pygame.display.set_mode((800, 600)) + pygame.key.set_repeat(500, 30) + self.font = pygame.font.Font(None, 16) + self.maxmapx = 39 + self.maxmapy = 35 + self.msgh = 32 + self.xtile = 16 + self.ytile = 16 + self.dirty =[] + self.colors = ( + (0x66, 0x66, 0x66), + (0xff, 0xff, 0xff), + (0x44, 0x44, 0x44), + (0x00, 0xff, 0x00), + (0x00, 0x66, 0x00), + (0xff, 0xff, 0x00), + (0x66, 0x44, 0x22), + (0xff, 0x00, 0x00), + (0x66, 0x00, 0x00), + (0x00, 0xff, 0xff), + (0x00, 0x66, 0x66) + ) + self.empty = pygame.Surface((self.xtile,self.ytile)) + self.empty.fill((0x00, 0x00, 0x00)) + 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() + + for tile in tiles: + tile["image"] = self.font.render(tile["char"], 1, self.colors[tile["color"]], (0x00, 0x00, 0x00)) + + 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] + tile = tiles[world.map[x][y]] + if flags & FLAG_VISIBLE or flags & FLAG_EXPLORED: + img = tile["image"] + 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]), 1, color) + else: + img = self.font.render("%s %s" % (chr(ord('a')+i), items[inv.inv[i]]['name']), 1, 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), 1, (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, 1, (0x00, 0x66, 0x00)) + self.screen.blit(img, (0, 0)) + text = "" + for m in msg.mesgs: + text += m + " " + img = self.font.render(text, 1, (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 + img = self.font.render(creat.char, 1, self.colors[creat.color], (0x00, 0x00, 0x00)) + self._blit_map(x, y, img) + + def centermap(self, pos): + print 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 pos!=None: + x,y = pos + self._blit_map(x, y, self.cursor, 0) + + 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: + msg.write("What does %s mean?" % name) + return (None, None) + +class DisplayCurses(Display): + def __init__(self, stdscr): + maxy, maxx = stdscr.getmaxyx() + self.msg = curses.newwin(1, maxx-16, 0, 0) + self.inv = curses.newwin(maxy-1, 16, 1, maxx-16) + self.stat = curses.newwin(1, 16, 0, maxx-16) + self.map = curses.newwin(maxy-1, maxx-16, 1, 0) + self.map.keypad(1) + self.map.immedok(0) + self.map.leaveok(0) + self.inv.immedok(0) + self.stat.immedok(0) + self.msg.immedok(0) + self.stat.scrollok(0) + self.msg.scrollok(1) + self.xmap = 0 + self.ymap = 0 + self.maxmapy, self.maxmapx = self.map.getmaxyx() + curses.meta(1) + if curses.has_colors(): + curses.start_color() + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_BLACK) + curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) + curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) + curses.init_pair(4, curses.COLOR_MAGENTA, curses.COLOR_BLACK) + curses.init_pair(5, curses.COLOR_RED, curses.COLOR_BLACK) + curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK) + self.colors = [ + curses.color_pair(0), + curses.color_pair(0) | curses.A_BOLD, + curses.color_pair(1) | curses.A_BOLD, + curses.color_pair(2) | curses.A_BOLD, + curses.color_pair(2), + curses.color_pair(3) | curses.A_BOLD, + curses.color_pair(3), + curses.color_pair(5) | curses.A_BOLD, + curses.color_pair(5), + curses.color_pair(6) | curses.A_BOLD, + curses.color_pair(6), + curses.color_pair(4), + ] + else: + self.colors = [ + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + curses.A_NORMAL, + ] + + def msg_draw(self, msg): + self.msg.clear() + for m in msg.last: + self.msg.addstr(m, self.colors[C_GREEN]) + self.msg.addstr(" ") + for m in msg.mesgs: + self.msg.addstr(m, self.colors[C_LIME]) + self.msg.addstr(" ") + + def _get_color(self, col): + if not self.colors: + return 0 + return self.colors[col] + + 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 + c = ord(creat.char) | self._get_color(creat.color) + try: + self.map.addch(y-self.ymap, x-self.xmap, c) + except: + pass + + def inv_draw(self, inv): + self.inv.clear() + for i in range(inv.max_inv): + if i in inv.eqp: + color = curses.A_BOLD | self.colors[C_LIME] + else: + color = curses.A_NORMAL | self.colors[C_GREEN] + if inv.charges[i]>0: + self.inv.addstr("%s %s %d\n" % (curses.unctrl(ord('a')+i), items[inv.inv[i]]['name'], inv.charges[i]), color) + else: + self.inv.addstr("%s %s\n" % (curses.unctrl(ord('a')+i), items[inv.inv[i]]['name']), color) + + def effect_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.effect!=None: + c = ord(effects[creat.effect]["char"]) | self._get_color(effects[creat.effect]["color"]) + else: + c = ord(creat.char) | self._get_color(creat.color) + try: + self.map.addch(y-self.ymap, x-self.xmap, c) + except: + pass + + 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] + tile = world.map[x][y] + c = ' ' + if flags & FLAG_VISIBLE: + c = ord(tiles[tile]["char"]) + try: + s = tiles[tile]["shadow"] + if world.flags[x-1][y-1] & FLAG_OPAQUE: + c |= self.colors[C_BLACK] + except KeyError: + c |= self._get_color(tiles[tile]["color"]) + elif flags & FLAG_EXPLORED: + c = ord(tiles[tile]["char"]) | self.colors[C_MAGENTA] + try: + if tiles[tile]["highlight"]: + c |= curses.A_BOLD + except KeyError: + pass + try: + s = tiles[tile]["shadow"] + if not world.flags[x-1][y-1] & FLAG_OPAQUE: + c |= curses.A_BOLD + except KeyError: + pass + try: + self.map.addch(y-self.ymap, x-self.xmap, c) + except: + pass + + 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 draw_stats(self, hero): + self.stat.clear() + wounds = '' + for i in range(hero.wounds): + wounds += '*' + self.stat.addstr(0, 0, "S:%3d W:%s" % (hero.stamina, wounds), self.colors[C_LRED] | curses.A_BOLD) + + def select(self, pos): + if pos==None: + self.map.move(0, 0) + try: + curses.curs_set(0) + except: + pass + else: + 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: + self.map.move(0, 0) + try: + curses.curs_set(0) + except: + pass + else: + try: + curses.curs_set(1) + except: + pass + self.map.move(y-self.ymap, x-self.xmap) + + def centermap(self, pos): + x, y = pos + sy, sx = self.map.getmaxyx() + self.xmap = -sx/2+x + self.ymap = -sy/2+y + + def scroll_map(self, pos, world): + x, y = pos + maxy, maxx = self.map.getmaxyx() + 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 clear(self): + self.msg.clear() + self.map.clear() + self.inv.clear() + self.stat.clear() + + def refresh(self): + self.msg.noutrefresh() + self.inv.noutrefresh() + self.stat.noutrefresh() + self.map.noutrefresh() + curses.doupdate() + + def world_draw(self, world): + self.map.clear() + for x in range(world.width): + for y in range(world.height): + self.tile_draw((x, y), world) + + def get_command(self, animate=0): + if animate: + self.map.timeout(300) + else: + self.map.timeout(-1) + try: + key = self.map.getkey() + except: + return (None, None) + if key=="-1": + return (None, None) + try: + return command_keys[key] + except KeyError: + msg.write("What does %s mean?" % key) + return (None, None) + +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 + self.char = zombie_char + self.color = zombie_color + world.creatures.append(self) + self.world.flags[self.pos[0]][self.pos[1]] |= FLAG_CREATURE + self.burning = 0 + self.dead = 0 + self.tripped = 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 + 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, MAP_CORPSE) + else: + self.world.add_tile(self.pos, MAP_CORPSE+1) + 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): + for dy in (-1, 0, 1): + for dx in (-1, 0, 1): + x = self.pos[0] + dx + y = self.pos[1] + dy + if self.world.map[x][y]==MAP_FLOOR: + if random.randrange(100)>110-amount: + self.world.add_tile((x,y), MAP_BLOOD) + elif random.randrange(100)>80-amount: + self.world.add_tile((x,y), MAP_BLOOD+1) + elif self.world.map[x][y]==MAP_WALL: + if random.randrange(100)>130-amount: + self.world.add_tile((x,y), MAP_BLOOD+2) + + 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): + 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): + self.burning = 1 + self.char = burning_char + self.color = burning_color + + def pushback(self): + 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: + 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.char = hero_char + self.color = hero_color + self.light = Light(world) + self.light.cast(self.pos[0], self.pos[1]) + self.inventory = Inventory() + 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 + + def kill(self): + 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: + 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: + msg.write("Ouch!") + self.bleed(20) + self.wound() + + def burn(self): + self.stamina -= random.randrange(10, 40) + msg.write("Burns!") + if self.stamina<0: + self.stamina = 0 + + def act_shoot(self): + if not self.target: + msg.write("Nobody here.") + raise ActError + gun_slot = self.inventory.eqp[KIND_GUN] + if gun_slot==None: + 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: + msg.write("Spak!") + creat.effect = EFF_MISS + self.inventory.useup_item(gun_slot) + self.disp.inv_draw(self.inventory) + else: + msg.write("Sneaky bastard!") + raise ActError + + def act_use(self, slot): + if self.inventory.inv[slot]==0: + 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: + msg.write("Useless.") + raise ActError + + def act_trash(self, slot): + if self.inventory.inv[slot]==0: + msg.write("Gone already.") + raise ActError + self.inventory.del_item(slot) + 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: + msg.write("There's nothing useful here.") + raise ActError + if self.world.map[x][y] == MAP_CRATE: + msg.write("Gently opening the crate...") + item = self.inventory.search_crate() + elif self.world.map[x][y] == MAP_CORPSE: + msg.write("Searching the corpse....") + item = self.inventory.search_corpse() + self.world.change(self.pos) + if item: + 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: + msg.write("Finally made it to safety!") + game_win(self.disp) + if self.world.flags[nx][ny] & FLAG_LETHAL: + msg.write("Careful! Those gears can shred you into pieces.") + raise ActError + if self.world.flags[nx][ny] & FLAG_BLOCKED: + 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: + 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: + msg.write('Pant, pant...') + + def act_aim(self): + if self.aim==None: + self.aim = 0 + start = self.aim + 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: + msg.write("Here.") + self.target = (x,y) + break + if self.aim == start: + 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(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(msg) + self.disp.refresh() + cmd, param = self.disp.get_command() + try: + if cmd == 'quit': + sys.exit() + elif cmd == 'refresh': + done = 0 + elif cmd == 'wait': + 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 + 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) + msg.clear() + +class Inventory: + def __init__(self): + self.max_inv = 21 + self.inv = [] + 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]: + 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: + 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: + msg.write(items[self.inv[slot]]["sound"]) + except KeyError: + pass + + +class GameError(Exception): + pass + +class ActError(Exception): + pass + + +def main(stdscr=None): + if stdscr: + maxy, maxx = stdscr.getmaxyx() + if maxy<24 or maxx<80: + raise GameError, "Screen too small, you need at last 80x24 characters." + disp = DisplayCurses(stdscr) + else: + 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() + +msg = Mesg() +try: +# raise ImportError + import curses + curses.wrapper(main) +except ImportError: + try: + import pygame + from pygame.locals import * + pygame.init() + main() + except ImportError: + raise GameError, "You need either PyGame or PyCurses." +msg.output()
