def create_unit(engine, name): unit_data = dict() entity = engine.entities.create() engine.ais.add(entity, AI()) engine.inputs.add(entity, Input(is_player=False)) engine.infos.add(entity, Information(name)) space = find_empty_space(engine) if not space: raise Exception("No empty space to place unit") engine.positions.add(entity, Position(*space)) # unit specific attributes if name == 'rat': render = Render(char='r') health = Health(2, 2) elif name == 'goblin': render = Render(char='g', color="brown") health = Health(5, 5) elif name == 'bat': render = Render(char='b') health = Health(3, 3) engine.renders.add(entity, render) engine.healths.add(entity, health)
def main(): d = {0: Position(1, 2)} with open('data.pickle', 'wb') as f: pickle.dump(d, f, pickle.HIGHEST_PROTOCOL) with open('data.pickle', 'rb') as f: data = pickle.load(f) print(data)
def convert_dungeon_to_ecs(self, map_id, map_type, dungeon): # add tiles and tile specific attribute components maptype = self.engine.tilemaptypes.shared[map_type] environment = ".#+/'" blocked = environment[1:3] doors = environment[2:4] for y, row in enumerate(dungeon): for x, char in enumerate(row): if char == ' ': continue # shared components tile_id = self.engine.entities.create() # tile self.engine.tiles.add(tile_id, Tile()) # visibility self.engine.visibilities.add(tile_id, Visibility()) # position self.engine.positions.add(tile_id, Position( x, y, map_id=map_id, movement_type=Position.MovementType.NONE, blocks=char in blocked )) # info info = self.engine.infos.shared[env_char_to_name[char]] self.engine.infos.add(tile_id, info) # render if char in ".'": colors = maptype.floors elif char == '#': colors = maptype.walls elif char in doors: colors = maptype.doors else: colors = maptype.stairs render = Render(char, random.choice(colors)) self.engine.renders.add(tile_id, render) # openable if char in doors: # This is a unique case (possibly more in the future) in # that this tile creates a map entity with Openable. openable = Openable(opened=char=='/') self.engine.openables.add(tile_id, openable)
if entity_id in self.components[component_type]: del self.components[entity_id] return True return False def find(self, entity_id: int, component_type: object) -> object: if component_type.manager not in self.components: raise ValueError( f"Cannot find {component_type} in component dictionary") return self.components[component_type.manager].get(entity_id) if __name__ == "__main__": from source.ecs.components import components, Position c = ComponentsManager(components) c.add(1, Position(1, 1)) print(c.find(1, Position)) print(c) # print memory footprint # Total allocated size: 2.4 KiB import tracemalloc tracemalloc.start() c = ComponentsManager(components) snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics('traceback') for stat in top_stats: print(stat) total = sum(stat.size for stat in top_stats) print("Total allocated size: %.1f KiB" % (total / 1024))
def initialize_cursor(self): # initialize a cursor self.cursor = self.engine.entities.create() self.engine.positions.add(self.cursor, Position())
def main(screen, mapstring): # setup colors curses.init_pair(1, curses.COLOR_MAGENTA, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK) dungeon = [[c for c in row] for row in mapstring.split('\n')] w, height = len(dungeon[0]), len(dungeon) cursor = Position(w // 2, height // 2) tiles = {(i, j): cell for j, row in enumerate(dungeon) for i, cell in enumerate(row)} a = None b = None changed = False path = None x_ruler = ''.join(str(i % 10) for i in range(w)) while True: screen.erase() screen.addstr(0, 0, mapstring) # add width ruler for x in (0, w + 1): screen.addstr(0, x, x_ruler) # add height ruler for j in range(len(dungeon)): screen.addch(j, 0, str(j % 10)) if a: screen.addstr(a.y, a.x, 'a') screen.addstr(height + 1, 0, f"a: {a.x:02}, {a.y:02}") if b: screen.addstr(b.y, b.x, 'b') screen.addstr(height + 2, 0, f"b: {b.x:02}, {b.y:02}") if a and b and changed: start = time.time() path = astar(tiles, a, b) screen.addstr(height + 6, 0, f"{time.time()-start}") changed = False if path: nheight = 0 for x, y in path: screen.addch(y, x, 'o', curses.color_pair(3)) screen.addstr(height, 0, f"c: {cursor.x:02}, {cursor.y:02}") screen.move(cursor.y, cursor.x) screen.refresh() char = screen.getch() keypress = keyboard[char] if keypress == 'q' or keypress == 'escape': break if keypress == 'up': cursor.y -= 1 if cursor.y > 1 else 0 elif keypress == 'down': cursor.y += 1 if cursor.y < len(dungeon) - 2 else 0 elif keypress == 'left': cursor.x -= 1 if cursor.x > 1 else 0 elif keypress == 'right': cursor.x += 1 if cursor.x < len(dungeon[0]) - 2 else 0 elif keypress == 'a': if not a or (a and a != cursor): a = Position(cursor.x, cursor.y) changed = True elif a and a == cursor: a = None elif keypress == 'b': if not b or (b and b != cursor): b = Position(cursor.x, cursor.y) changed = True elif b and b == cursor: b = None elif keypress == 'c': a = None b = None changed = False path = None
def create_player(): player = engine.entities.create() engine.player = player engine.inputs.add(player, Input()) if not spaces: raise Exception("No empty spaces to place player") space = spaces.pop() engine.positions.add( player, Position(*space, map_id=engine.world.id, movement_type=Position.MovementType.GROUND)) engine.renders.add(player, Render('@')) engine.healths.add(player, Health(10, 20)) engine.manas.add(player, Mana(10, 20)) engine.infos.add(player, Information("Hero")) engine.inputs.add(player, Input(needs_input=True)) # create armor for player # head helmet = engine.entities.create() engine.items.add(helmet, Item('armor', ('head', ))) engine.renders.add(helmet, Render('[')) engine.infos.add(helmet, Information('iron helmet', 'Helps protect your head.')) engine.armors.add(helmet, Armor(2)) # body platemail = engine.entities.create() engine.items.add(platemail, Item('armor', ('body', ))) engine.renders.add(platemail, Render('[')) engine.infos.add( platemail, Information('platemail', 'Armor made from sheets of metal. Heavy but durable.')) engine.armors.add(platemail, Armor(5)) # feet ironboots = engine.entities.create() engine.items.add(ironboots, Item('armor', ('feet', ))) engine.renders.add(ironboots, Render('[')) engine.infos.add(ironboots, Information('iron boots', 'Reinforced footwear.')) engine.armors.add(ironboots, Armor(3)) # create a weapon for player spear = engine.entities.create() engine.items.add(spear, Item('weapon', ('hand', 'missiles'))) engine.renders.add(spear, random.choice(engine.renders.shared['spear'])) engine.infos.add(spear, engine.infos.shared['spear']) engine.weapons.add(spear, Weapon(4, 3)) # create some missiles for player stone = engine.entities.create() engine.items.add(stone, Item('weapon', ('hand', 'missiles'))) engine.renders.add(stone, Render('*')) engine.infos.add( stone, Information('stone', 'A common item useful for throwing.')) engine.weapons.add(stone, Weapon(1)) # add created items to an equipment component e = Equipment(head=helmet, body=platemail, hand=spear, feet=ironboots, missiles=stone) engine.equipments.add(player, e) # add an inventory i = Inventory() engine.inventories.add(player, Inventory())
def test_position_add(): p = Position(2, 3) q = Position(5, 1) assert p + q == Position(7, 4)
def missile(self, cursor): end = self.engine.positions.find(cursor) cursor_component = self.engine.cursors.find(cursor) start = self.engine.positions.find(cursor_component.entity) equipment = self.engine.equipments.find(cursor_component.entity) if equipment.missile_weapon and equipment.missiles: ... elif equipment.missiles: # save missiles item id missiles = equipment.missiles # remove stone from inventory unequipped = self.engine.router.route('equipment', 'unequip_item', cursor_component.entity, missiles) # processed correctly if unequipped: render = self.engine.renders.find(missiles) self.engine.logger.add(render.char) path = pathfind(self.engine, start, end, pathfinder=bresenhams) # add a position component to thrown item self.engine.positions.add( missiles, Position(*path[-1], self.engine.world.id, movement_type=Position.MovementType.NONE, blocks=None)) # check if there is a unit in the path of the throw item # if there is one then calculate damage. # Do this by: # 1. Getting all units in the missile path[1:] # Note: this works since only one unit will be on a # tile at any given time # 2 Check each position in the path against the unit # positions. # 3. If a unit exists then calculate chance to hit. # 4. If hit then calculate damage applied. units = {(position.x, position.y): (uid, health) for uid, (health, position) in join( self.engine.healths, self.engine.positions) if (position.x, position.y) in path[1:]} # skip altogether if no units found in the unit query log = [] is_player = cursor_component.entity == self.engine.player weapon = self.engine.infos.find(missiles) if units: for p in path[1:]: uid, health = units.get(p, (None, None)) if not uid: continue attacker = self.engine.infos.find( cursor_component.entity) attack = self.engine.weapons.find( missiles).damage_throw defender = self.engine.infos.find(uid) if is_player: log.append( f"You throw the {weapon.name} at {defender.name}" ) else: log.append( f"The {attacker.name} throws a {weapon.name} at {defender.name}" ) # calculate chance to hit # NOTE: calculate with a skill based chance later # currently 50% chance to hit if True: armor = self.engine.router.route( 'armor', 'get_total_armor_value', uid) damage = max(0, attack - armor) health.cur_hp -= damage if damage < 1: log.append(f", but the missile did no damage!") else: log.append(f". It hits for {damage} damage.") if health.cur_hp < 1: log.append(f" The {defender.name} dies!") self.engine.destroyed.add(uid, Destroyed()) else: if is_player: log.append(f", but miss!") else: log.append(f", but misses.") else: if is_player: log.append( f"You throw the {weapon.name} at nothing in particular." ) self.engine.logger.add(''.join(log)) # add ranged hit effect to effect list effect = RangeHitEffect(cursor, render.char, '0', path) self.engine.effects.add(cursor_component.entity, effect) return True
# define world w = engine.entities.create() engine.world = WorldGraph({w: DungeonNode(w)}, w) # define map properties t = TileMap(x, y) engine.tilemaps.add(w, t) # define map objects for y, row in enumerate(m): for x, cell in enumerate(row): e = engine.entities.create() engine.positions.add(e, Position( x, y, map_id=w, blocks=cell is '#' )) engine.visibilities.add(e, Visibility()) # define player e = engine.entities.create() engine.player = e # define random unblocked position walkable = [ (p.x, p.y) for p in engine.positions.components.values() if not p.blocks ] random.shuffle(walkable)