#!/usr/bin/python3 import collections as cl import networkx as nx import curses as c import random from time import sleep from grid import Grid, bresenhams from fov import fieldOfView class Tile(): def __init__(self, glyph, name, description, solid=False, transparent=True, color=c.COLOR_WHITE): self.appearance = glyph self.name = name self.description = description self.solid = solid self.transparent = transparent self.color = color def draw(self): return (self.glyph, self.color) def describe(self): return self.description class World(Grid): def __init__(self, window, width, height, initial_tile): super().__init__(width, height, initial_tile) self.run = True self.window = window self.width = width self.height = height self.bodies = [] self.behavior = [] class Behaviour(): def __init__(self, name): self.name = name self.body = None self.world = None def behave(self): pass def incarnate(self, body): if not self.body is None: self.body.behavior = None if not body.behavior is None: body.behavior.body = None self.body = body body.behavior = self def message(self, msg): pass class Player(Behaviour): def __init__(self, name): super().__init__(name) self.msgbuffer = cl.deque([], 200) self.messaged = False self.messaged_last = False self.realtime = False def message(self, msg): if len(self.msgbuffer) > 0 and msg == self.msgbuffer[0][1] and self.messaged_last: self.msgbuffer.appendleft((self.msgbuffer.popleft()[0] + 1, msg)) else: self.msgbuffer.appendleft((1, msg)) self.messaged = True def behave(self): # calculate visibility visibility = Grid(self.world.width, self.world.height, False) fieldOfView(self.body.x, self.body.y, self.world.width, self.world.height, 100, lambda x, y: visibility.set(x, y, True), lambda x, y: self.world.get(x, y).solid) self.world.window.erase() # draw interface you for x in range(0, self.world.width): for y in range(0, self.world.height): if visibility.get(x, y): self.world.window.addch(y, x, self.world.get(x, y).appearance) for body in self.world.bodies: if visibility.get(body.x, body.y): body.draw() if self.messaged: if self.msgbuffer[0][0] == 1: self.world.window.addstr(self.world.height + 1, 0, self.msgbuffer[0][1]) else: self.world.window.addstr(self.world.height + 1, 0, self.msgbuffer[0][1] + " (x" + str(self.msgbuffer[0][0]) + ")") self.messaged = False self.messaged_last = True else: self.messaged_last = False self.world.window.refresh() # this is the one gameplay input point. if self.realtime: sleep(0.2) try: key = self.world.window.getkey() except c.error: key = "" c.flushinp() else: key = self.world.window.getkey() match key: case 'w': if not self.body.move((0, -1)): self.message("You cannot go there.") case 's': if not self.body.move((0, +1)): self.message("You cannot go there.") case 'a': if not self.body.move((-1, 0)): self.message("You cannot go there.") case 'd': if not self.body.move((+1, 0)): self.message("You cannot go there.") case 'c': coords = self.world.neighbors(self.body.x, self.body.y) for coord in coords: x, y = coord t = self.world.get(x, y) if t.name == "open door": self.body.close_door(x, y) break else: self.message("You see nothing here to close.") case 'q': self.world.run = False case 'r': if self.realtime: self.realtime = False self.world.window.nodelay(False) else: self.realtime = True self.world.window.nodelay(True) # case _: self.world.message(key) class Drunkard(Behaviour): def behave(self): while True: dx, dy = random.randint(-1,1), random.randint(-1,1) tx, ty = dx + self.body.x, dy + self.body.y if self.world.in_bounds(tx, ty) and not self.world.get(tx, ty).solid: break bodies = [body for body in self.world.bodies if body.x == tx and body.y == ty] if bodies: for tb in bodies: tb.behavior.message("A " + self.body.name + " bumps into you.") else: self.body.move((dx, dy)) class Body(): def __init__(self, name, appearence, description, color): self.name = name self.appearence = appearence self.description = description self.color = color self.behavior = None self.world = None self.x = None self.y = None def move(self, direction): x, y = direction tx, ty = self.x + x, self.y + y if not self.world.in_bounds(tx, ty): return False elif self.world.get(tx, ty).name == "closed door": self.open_door(tx, ty) return True elif self.world.get(tx, ty).solid: return False else: self.x = tx self.y = ty return True def open_door(self, x, y): if not self.world.in_bounds(x, y): self.behavior.message("You cannot open anything there.") return False elif self.world.get(x, y).name == "closed door": self.world.set(x, y, door_open) self.behavior.message("You open the door.") return True else: self.behavior.message("You cannot open anything there.") return False def close_door(self, x, y): if not self.world.in_bounds(x, y): self.behavior.message("You cannot close anything there.") return False elif self.world.get(x, y).name == "open door": self.world.set(x, y, door_closed) self.behavior.message("You close the door.") return True else: self.behavior.message("You cannot close anything there.") return False def describe(self): return self.description def draw(self): self.world.window.addch(self.y, self.x, self.appearence) class Conversation(list): def __init__(self, title, description): self.title = title self.description = description def run(self): # rendering code goes here pass class Response(): def __init__(self, name, text, target=None, prerequisite=None, function=None): self.name = name self.text = text self.target = target self.function = function def respond(self, title, description): # rendering code goes here if function: function() if target: target.run() bum_conversation = Conversation("Conversing With a Drunkard", "A foul-smelling drunkard waves to and fro, trying to form words. His tattered clothes are brown with filth.") bum_conversation.append(Response("Hello? Is everything all right?", "Hargl... hurgl...", target=bum_conversation)) bum_conversation.append(Response("I will take my leave, good sir.", "...")) p = Player("you") pb = Body("yourself", '@', "an unremarkable person", c.COLOR_WHITE) p.incarnate(pb) floor = Tile('.', "floor", "an unremarkable piece of floor") wall = Tile('#', "wall", "an unremarkable wall", solid=True, transparent=False) door_closed = Tile('+', "closed door", "an unremarkable door", solid=True, transparent=False) door_open = Tile(',', "open door", "an unremarkable door", solid=False, transparent=True) w = c.initscr() c.curs_set(0) c.raw() c.echo(False) wld = World(w, 80, 23, floor) drunk = Drunkard("drunkard") db = Body("drunkard", 'H', "A smelly drunkard, stumbling about.", c.COLOR_WHITE) drunk.incarnate(db) db.world = wld drunk.world = wld db.x = 20 db.y = 16 wld.behaviors = [p, drunk] wld.bodies = [pb, db] pb.world = wld p.world = wld pb.x = 3 pb.y = 6 wld.set(12, 12, wall) wld.set(13, 12, wall) wld.set(14, 12, wall) wld.set(15, 12, wall) wld.set(16, 12, wall) wld.set(16, 13, wall) wld.set(16, 14, wall) wld.set(18, 12, wall) wld.set(18, 13, wall) wld.set(18, 14, wall) wld.set(12, 13, door_closed) while wld.run: for behavior in wld.behaviors: behavior.behave() c.endwin()