diff --git a/__pycache__/dungeon.cpython-310.pyc b/__pycache__/dungeon.cpython-310.pyc new file mode 100644 index 0000000..a3c5228 Binary files /dev/null and b/__pycache__/dungeon.cpython-310.pyc differ diff --git a/__pycache__/enemies.cpython-310.pyc b/__pycache__/enemies.cpython-310.pyc new file mode 100644 index 0000000..268a583 Binary files /dev/null and b/__pycache__/enemies.cpython-310.pyc differ diff --git a/__pycache__/map.cpython-310.pyc b/__pycache__/map.cpython-310.pyc new file mode 100644 index 0000000..0f1d390 Binary files /dev/null and b/__pycache__/map.cpython-310.pyc differ diff --git a/__pycache__/player.cpython-310.pyc b/__pycache__/player.cpython-310.pyc new file mode 100644 index 0000000..85cbfa9 Binary files /dev/null and b/__pycache__/player.cpython-310.pyc differ diff --git a/__pycache__/projectile.cpython-310.pyc b/__pycache__/projectile.cpython-310.pyc new file mode 100644 index 0000000..01968ae Binary files /dev/null and b/__pycache__/projectile.cpython-310.pyc differ diff --git a/__pycache__/strategy.cpython-310.pyc b/__pycache__/strategy.cpython-310.pyc new file mode 100644 index 0000000..782e5e8 Binary files /dev/null and b/__pycache__/strategy.cpython-310.pyc differ diff --git a/__pycache__/wall.cpython-310.pyc b/__pycache__/wall.cpython-310.pyc new file mode 100644 index 0000000..8f93925 Binary files /dev/null and b/__pycache__/wall.cpython-310.pyc differ diff --git a/angband.py b/angband.py new file mode 100644 index 0000000..4a15517 --- /dev/null +++ b/angband.py @@ -0,0 +1,44 @@ +from retro.game import Game +from player import Player +from dungeon import Dungeon +from random import sample +from wall import Wall +from map import ( + board_edges, + inner_board, + level_one, + random_empty_position +) +from enemies import ( + Orc, + Rat, + Spider +) + +print("Welcome to AngBAD (a poor representation of Angband)!\n") + +race = input("Choose your race (Human, Elf, Dwarf): ").capitalize() +while race not in ["Human", "Elf", "Dwarf"]: + print("Invalid race. Please choose Human, Elf, or Dwarf.") + race = input("Choose your race (Human, Elf, Dwarf): ").capitalize() + +class_ = input("Choose your class (Warrior, Mage, Rogue): ").capitalize() +while class_ not in ["Warrior", "Mage", "Rogue"]: + print("Invalid class. Please choose Warrior, Mage, or Rogue.") + class_ = input("Choose your class (Warrior, Mage, Rogue): ").capitalize() + +print(f"\nYou've chosen to play as a {race} {class_}.") +input("Press Enter to continue. Good luck!") + +board_size = (50,25) +x,y = board_size + +walls = [Wall(position) for position in board_edges(board_size)] +level = [Wall(position) for position in level_one(board_size)] +game = Game(walls + level, {"Race":race, "Class":class_,}, board_size = board_size) +game.add_agent(Player((x//2,y//2),race,class_)) +game.add_agent(Orc(random_empty_position(game))) +game.add_agent(Rat(random_empty_position(game))) +game.add_agent(Spider(random_empty_position(game))) + +game.play() \ No newline at end of file diff --git a/dungeon.py b/dungeon.py new file mode 100644 index 0000000..585f823 --- /dev/null +++ b/dungeon.py @@ -0,0 +1,27 @@ +# dungeon.py +# ------------ +# By Pat Wick +# This module defines a dungeon generation algorithm. I +# still need to figure out what that might actually mean + +class Dungeon: + board_size = (10,10) + board_width = 10 + board_height = 10 + position = (0,0) + dungeon_map = [["."] * board_width] * board_height + + + for row in range(board_height): + for col in range(board_width): + if row == 0 or row == board_height-1: + dungeon_map[row][col] = "#" + else: + if col == 0 or col == board_width-1: + dungeon_map[row][col] = "#" + else: + dungeon_map[row][col] = "." + + def __init__(self, position): + self.position = position + self.name = "dungeon" \ No newline at end of file diff --git a/enemies.py b/enemies.py new file mode 100644 index 0000000..a6dd311 --- /dev/null +++ b/enemies.py @@ -0,0 +1,73 @@ +from strategy import ( + random_move, + move_toward_player, +) + +class Orc: + character = "O" + hp = 20 + deadly = True + speed = 25 + + def __init__(self,position): + self.position = position + + def play_turn(self, game): + if game.turn_number % self.speed == 0: + move = move_toward_player(self.position, game) + if move: + x, y = self.position + dx, dy = move + self.position = (x + dx, y + dy) + if self.position == game.get_agent_by_name("player").position: + game.state['message'] = "Yum." + game.end() + + if self.hp <= 0: + game.remove_agent(self) + +class Rat: + character = "R" + hp = 2 + deadly = True + speed = 15 + + def __init__(self, position): + self.position = position + + def play_turn(self, game): + if game.turn_number % self.speed == 0: + move = random_move(self.position, game) + if move: + x, y = self.position + dx, dy = move + self.position = (x + dx, y + dy) + if self.position == game.get_agent_by_name("player").position: + game.state['message'] = "Eep." + game.end() + + if self.hp <= 0: + game.remove_agent(self) + +class Spider: + character = "S" + hp = 5 + deadly = True + speed = 5 + + def __init__(self,position): + self.position = position + + def play_turn(self, game): + if game.turn_number % self.speed == 0: + move = random_move(self.position, game) + if move: + x, y = self.position + dx, dy = move + self.position = (x + dx, y + dy) + if self.position == game.get_agent_by_name("player").position: + game.state['message'] = "Hsssss." + game.end() + + if self.hp <= 0: + game.remove_agent(self) \ No newline at end of file diff --git a/map.py b/map.py new file mode 100644 index 0000000..89597ad --- /dev/null +++ b/map.py @@ -0,0 +1,61 @@ +from retro.game import Game +from random import sample +from player import Player +from wall import Wall +from random import randint + +def board_edges(board_size): + x,y = board_size + positions = [] + top = [(i,0) for i in range(x)] + bottom = [(i,y-1) for i in range(x)] + left = [(0,j) for j in range(1,y-1)] + right = [(x-1,j) for j in range(1,y-1)] + return top + bottom + left + right + +def inner_board(board_size): + x,y = board_size + positions = [] + for i in range(1,x-1): + for j in range(1,y-1): + positions.append((i,j)) + return positions + +def random_empty_position(game): + """Returns a random empty position. + """ + agents_by_position = game.get_agents_by_position() + while True: + x, y = game.board_size + i = randint(1, x-2) + j = randint(1, y-2) + if not agents_by_position[(i,j)]: + return (i,j) + +def level_one(board_size): + x,y = board_size + positions = [] + for i in range(1,x-1): + for j in range(1,y//4): + if i <= x // 4 or i >= x - (x // 4): + positions.append((i,j)) + for i in range(1,x//4): + for j in range((y - (y // 4)), y-1): + positions.append((i,j)) + + # for i in range(1,x-1): + # for j in range(1,y-1): + # if i >=4 and i <= 7 or i >= 13 and i <= 16: + # if j >= 4 and j <= 7 or j >= 13 and j <= 16: + # positions.append((i,j)) + return positions + +def level_two(board_size): + x,y = board_size + positions = [] + for i in range(1,x-1): + for j in range(1,y-1): + if i >=4 and i <= 7 or i >= 13 and i <= 16: + if j >= 4 and j <= 7 or j >= 13 and j <= 16: + positions.append((i,j)) + return positions \ No newline at end of file diff --git a/player.py b/player.py new file mode 100644 index 0000000..d6bfcc5 --- /dev/null +++ b/player.py @@ -0,0 +1,111 @@ +# player.py +# ------------ +# By Pat Wick +# This module defines a player agent class. This is intended +# to be used in an implementation of an adventure game but could +# generally be adapted for other player character uses. + +from retro.agent import ArrowKeyAgent +from retro.game import Game +from projectile import Projectile + +class Player: + name = "player" + level = 1 + xp = 0 + direction = (1,0) + class_ = "" + speed = 0 + damage = 0 + + def __init__(self, position, race, class_): + """Class and race will determine player stats and abilities.""" + self.position = position + self.race = race + self.class_ = class_ + if class_.capitalize() == "Warrior": + self.color = "red" + self.class_ == class_ + elif class_.capitalize() == "Rogue": + self.color = "green" + self.class_ == class_ + else: + self.color = "blue" + self.class_ == class_ + + if race.capitalize() == "Human": + self.character = "H" + self.speed = 3 + self.damage = int(2 + (self.level / 3)) + elif race.capitalize() == "Elf": + self.character = "E" + self.speed = 1 + self.damage = int(1 + (self.level / 4)) + else: + self.character = "D" + self.speed = 6 + self.damage = int(3 + (self.level / 2)) + + def attack(self,game): + if self.class_ == "Warrior": + if game.turn_number % self.speed == 0: + agent = self.get_agent_in_position((self.position[0] + self.direction[0],self.position[1] + self.direction[1]),game) + if agent: + if agent.deadly: + agent.hp -= game.get_agent_by_name("player").damage * 2 + else: + projectile = Projectile((self.position[0] + self.direction[0],self.position[1] + self.direction[1]), self.direction, self.speed, game) + game.add_agent(projectile) + #print("pew pew pew") + + def handle_keystroke(self, keystroke, game): + x, y = self.position + if keystroke.name in ("KEY_LEFT", "KEY_RIGHT"): + if keystroke.name == "KEY_LEFT": + new_position = (x - 1, y) + self.direction = (-1,0) + else: + new_position = (x + 1, y) + self.direction = (1,0) + if game.on_board(new_position): + self.try_to_move(new_position,game) + if game.is_empty(new_position): + self.position = new_position + + if keystroke.name in ("KEY_DOWN", "KEY_UP"): + if keystroke.name == "KEY_DOWN": + new_position = (x, y + 1) + self.direction = (0,1) + else: + new_position = (x, y - 1) + self.direction = (0,-1) + if game.on_board(new_position): + self.try_to_move(new_position,game) + if game.is_empty(new_position): + self.position = new_position + + if keystroke == " ": + self.attack(game) + + def try_to_move(self, position, game): + agent = self.get_agent_in_position(position,game) + if agent: + if agent.deadly: + game.state['message'] = "Monsters can be deadly..." + game.end() + + + def get_agent_in_position(self, position, game): + + agents = game.get_agents_by_position()[position] + if agents: + return agents[0] + + def level_up(self): + xpToLevel = (self.level + self.level - 1) * 30 + if self.xp >= xpToLevel: + self.xp -= xpToLevel + self.level += 1 + + def play_turn(self,game): + self.level_up() \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..ddb1526 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +package = [] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "53f2eabc9c26446fbcc00d348c47878e118afc2054778c3c803a0a8028af27d9" diff --git a/projectile.py b/projectile.py new file mode 100644 index 0000000..2c7c749 --- /dev/null +++ b/projectile.py @@ -0,0 +1,53 @@ +from retro.game import Game + +class Projectile: + character = "*" + deadly = False + + def __init__(self, position, direction, speed, game): + self.position = position + self.direction = direction + self.speed = speed + if game.get_agent_by_name("player").class_ == "Rogue": + if self.direction in [(1,0), (-1,0)]: + self.character = "-" + elif self.direction in [(0,1), (0,-1)]: + self.character = "|" + + def move(self, game): + """Try to move in direction set by player when launched. If blocked, + disappear.""" + dx, dy = self.direction + new_position = (self.position[0] + dx, self.position[1] + dy) + if game.on_board(new_position): + if game.is_empty(new_position): + self.position = new_position + else: + agent = self.get_agent_in_position(new_position,game) + if agent: + if agent.deadly: + agent.hp -= game.get_agent_by_name("player").damage + game.remove_agent(self) + else: + game.remove_agent(self) + else: + game.remove_agent(self) + + def play_turn(self,game): + if game.turn_number % self.speed == 0: + self.move(game) + + def get_agent_in_position(self, position, game): + """Returns an agent at the position, or returns None. + game.get_agents_by_position always returns a list, which may + contain zero, one, or multiple agents at the given position. + In the Beast game, we never allow more than one agent to be in + a position. + """ + agents = game.get_agents_by_position()[position] + if agents: + return agents[0] + + def handle_collision(self, game): + # need to fix this at some point + pass \ No newline at end of file diff --git a/strategy.py b/strategy.py new file mode 100644 index 0000000..61074e3 --- /dev/null +++ b/strategy.py @@ -0,0 +1,57 @@ +from random import choice + +direction_vectors = [(0, 1), (1, 0), (0, -1), (-1, 0)] + +def possible_moves(position, game): + "Returns a list of vectors to empty spaces" + agents_by_position = game.get_agents_by_position() + possible_moves = [] + for vector in direction_vectors: + x, y = position + dx, dy = vector + new_position = (x + dx, y + dy) + if not agents_by_position[new_position]: + possible_moves.append(vector) + return possible_moves + +def random_move(position, game): + "Returns a random vector representing a move to an empty space from position" + moves = possible_moves(position, game) + if moves: + return choice(moves) + +def distance(p0, p1): + """Returns the 'manhattan distance' from one position to another + The 'manhattan distance' describes the distance from one point to another + on a city grid, where you can only go horizontally and vertically, not + diagonally. + """ + x0, y0 = p0 + x1, y1 = p1 + return abs(x1 - x0) + abs(y1 - y0) + +def move_toward_player(position, game): + "Returns a move which will come closest to the player" + player_position = game.get_agent_by_name("player").position + moves = possible_moves(position, game) + moves_with_distance = [] + for vector in moves: + x, y = position + dx, dy = vector + new_position = (x + dx, y + dy) + distance_to_player = distance(new_position, player_position) + moves_with_distance.append((distance_to_player, vector)) + if moves_with_distance: + shortest_distance, best_move = sorted(moves_with_distance)[0] + return best_move + +def move_to_player(position, game): + player_position = game.get_agent_by_name("player").position + for vector in direction_vectors: + x, y = position + dx, dy = vector + new_position = (x + dx, y + dy) + if new_position == player_position: + return vector + + diff --git a/wall.py b/wall.py new file mode 100644 index 0000000..7041db8 --- /dev/null +++ b/wall.py @@ -0,0 +1,7 @@ +class Wall: + #name = "wall" + character = "█" + deadly = False + + def __init__(self,position): + self.position = position \ No newline at end of file