def create_river(self, tile): center_x = self.width // 2 center_y = self.height // 2 # from one side towards the other configs = [(center_x, 0, 90), (center_x, self.height - 1, -90), (0, center_y, 0), (self.width - 1, center_y, 180)] choice = rl.random_int(0, len(configs) - 1) x, y, angle = configs[choice] #x, y, angle = center_x, center_y, rl.random_int(0, 359) width = 2 finished = False while not finished: #if player.distance(x, y) < 10: # break dx = int(math.cos(math.pi * angle / 180) * 5) dy = int(math.sin(math.pi * angle / 180) * 5) for i, j in util.line_iter(x, y, x + dx, y + dy): if i < 0 or i >= self.width or j < 0 or j >= self.height: finished = True break self.fill_circle(i, j, width, tile) x += dx y += dy angle += rl.random_int(0, 60) - 30 width += (rl.random_int(0, 100) - 50) / 100 if width < 2: width = 2 if width > 5: width = 5
def place_objects(level, room): # this is where we decide the chance of each monster or item appearing. # maximum number of monsters per room max_monsters = from_dungeon_level([[1, 1], [1, 2], [2, 3], [2, 4], [3, 5], [2, 6], [1, 7], [0, 8]]) #max_monsters = game.dungeon_level # maximum number of items per room max_items = from_dungeon_level([[4, 1], [12, 2]]) # chance of each item (by default they have a chance of 0 at level 1, which then goes up) item_chances = {} #item_chances['heal'] = 35 # healing potion always shows up, even if all other items have 0 chance item_chances['lightning'] = from_dungeon_level([[25, 1]]) item_chances['fireball'] = from_dungeon_level([[25, 1]]) item_chances['confuse'] = from_dungeon_level([[10, 1]]) item_chances['sword'] = from_dungeon_level([[5, 1]]) item_chances['shield'] = from_dungeon_level([[15, 1]]) # choose random number of monsters num_monsters = rl.random_int(1, max_monsters) monster_type = random_choice(get_monster_chances()) for i in range(num_monsters): # choose random spot for this monster x = rl.random_int(room.x1 + 1, room.x2 - 1) y = rl.random_int(room.y1 + 1, room.y2 - 1) # only place it if the tile is not blocked if not level.is_blocked(x, y): level.objects.append(make_monster(monster_type, x, y))
def attack(self, target): if not self.is_hostile(target): print('warning, %s trying to attack friend %s' % (self.owner.name, target.name)) return # a simple formula for attack damage damage = rl.random_int(0, self.power) - rl.random_int( 0, target.defense) print('attack:', self, target, self.power, target.defense, damage) if damage > 0: #damage = rl.random_int(1, damage) # make the target take some damage if player in [self, target] or player.can_see(self): ui.message( util.capitalize(self.get_name('attack{s}')) + ' ' + target.get_name() + ' for ' + str(damage) + ' hit points.') else: ui.message('You hear combat in the distance', rl.LIGHTGRAY) target.take_damage(damage, self) else: if player in [self, target] or player.can_see(self): ui.message( util.capitalize(self.get_name('attack{s}')) + ' ' + target.get_name() + ' but it has no effect!')
def control(self, monster): if self.num_turns > 0: # still confused... # move in a random direction, and decrease the number of turns confused self.num_turns -= 1 monster.move(rl.random_int(-1, 1), rl.random_int(-1, 1)) else: # restore the previous AI (this one will be deleted because it's not referenced anymore) monster.pop_controller() ui.message( util.capitalize(monster.get_name('{is}')) + ' no longer confused!', rl.RED) monster.controller.control(monster)
def make_map(self, max_rooms, room_min_size, room_max_size, map_width, map_height, player): rooms = [] num_rooms = 0 for r in range(max_rooms): # random width and height w = rl.random_int(room_min_size, room_max_size) h = rl.random_int(room_min_size, room_max_size) # random position without going out of the boundaries of the map x = rl.random_int(0, map_width - w - 1) y = rl.random_int(0, map_height - h - 1) # "Rect" class makes rectangles easier to work with new_room = Rect(x, y, w, h) # run through the other rooms and see if they intersect with this one for other_room in rooms: if new_room.intersect(other_room): break else: # this means there are no intersections, so this room is valid # "paint" it to the map's tiles self.create_room(new_room) # center coordinates of new room, will be useful later (new_x, new_y) = new_room.center() if num_rooms == 0: # this is the first room, where the player starts at player.x = new_x player.y = new_y else: # all rooms after the first: # connect it to the previous room with a tunnel # center coordinates of previous room (prev_x, prev_y) = rooms[num_rooms - 1].center() # flip a coin (random number that is either 0 or 1) if rl.random_int(0, 1) == 1: # first move horizontally, then vertically self.create_h_tunnel(prev_x, new_x, prev_y) self.create_v_tunnel(prev_y, new_y, new_x) else: # first move vertically, then horizontally self.create_v_tunnel(prev_y, new_y, prev_x) self.create_h_tunnel(prev_x, new_x, new_y) # finally, append the new room to the list rooms.append(new_room) num_rooms += 1
def generate_level(num_rooms, template_subset): rooms = [Room(template_subset)] fails = 0 while len(rooms) < num_rooms: a = rooms[rl.random_int(0, len(rooms) - 1)] door = a.doors[rl.random_int(0, len(a.doors) - 1)] if not door.used: b = Room(template_subset) other_door = b.doors[rl.random_int(0, len(b.doors) - 1)] b.x = a.x + door.x - other_door.x b.y = a.y + door.y - other_door.y ok = True for i, room in enumerate(rooms): if intersect(room, b): ok = False if ok: door.used = True other_door.used = True rooms.append(b) fails = 0 else: fails += 1 if fails > 100: break min_x = rooms[0].x min_y = rooms[0].y max_x = rooms[0].x + rooms[0].width max_y = rooms[0].y + rooms[0].height for a in rooms[1:]: if a.x < min_x: min_x = a.x if a.x + a.width > max_x: max_x = a.x + a.width if a.y < min_y: min_y = a.y if a.y + a.height > max_y: max_y = a.y + a.height width = max_x - min_x height = max_y - min_y level = rl.Array(width, height) for room in rooms: room.bake(level, -min_x, -min_y) for j in range(height): for i in range(width): if level[i, j] == 3: if i == 0 or i == width - 1 or j == 0 or j == height - 1 \ or level[i - 1, j] == 0 or level[i + 1, j] == 0 or level[i, j - 1] == 0 or level[i, j + 1] == 0 \ or level[i - 1, j - 1] == 0 or level[i + 1, j - 1] == 0 or level[i - 1, j + 1] == 0 or level[i + 1, j + 1] == 0: level[i, j] = 2 level.replace(3, 1) level.replace(0, 2) return level
def make_forest_map(): level = Level(const.MAP_WIDTH, const.MAP_HEIGHT) level.tiles.fill(Tile.GRASS) level.blocked.fill(0) center_x = level.width // 2 center_y = level.height // 2 radius = min([center_x, center_y]) def contains_tree(x, y): d = math.sqrt((x - center_x)**2 + (y - center_y)**2) if d < 3: return False if d >= radius: return True p = 100 * d / radius v = rl.random_int(0, 100) return v < p level.objects = [player] player.x = center_x player.y = center_y for y in range(level.height): for x in range(level.width): if contains_tree(x, y): level[x, y] = Tile.mapping[Tile.WALL] #level.objects.append(actors.Actor(x, y, graphics.TREE, 'tree', rl.GREEN, always_visible=True)) #level.blocked[x, y] = 1 level.blocked = level.tiles.equals(Tile.WALL) angle = rl.random_int(0, 360) distance = rl.random_int(2, 3) level.stairs = actors.Actor(center_x + int(distance * math.cos(angle)), center_y + int(distance * math.sin(angle)), graphics.ROCK_STAIRS, 'stairs', rl.WHITE, always_visible=True, z=-2) level.objects.append(level.stairs) level.compute_fov() #distance = level.blocked.copy() #distance.replace(0, rl.INT_MAX) #distance.replace(1, -1) #distance[player.x, player.y] = 0 #distance.dijkstra() #distance.replace(rl.INT_MAX, -1) #print(distance.max(), distance.argmax()) return level
def random_choice_index(chances): dice = rl.random_int(1, sum(chances)) running_sum = 0 choice = 0 for w in chances: running_sum += w if dice <= running_sum: return choice choice += 1
def contains_tree(x, y): d = math.sqrt((x - center_x)**2 + (y - center_y)**2) if d < 3: return False if d >= radius: return True p = 100 * d / radius v = rl.random_int(0, 100) return v < p
def summon_monster(name): print(name) x, y = 0, 0 i = 0 while i < 100: angle = rl.random_int(0, 359) distance = rl.random_int(2, 4) x = int(caster.x + .5 + math.cos(math.pi * angle / 180) * distance) y = int(caster.y + .5 + math.sin(math.pi * angle / 180) * distance) print(x, y) if not level.is_blocked(x, y) and caster.can_see_tile(x, y): break i += 1 if i == 100: if caster is player: ui.message('You fail to cast the summon spell.', rl.BLUE) monster = monsters.make_monster(name, x, y) if not hostile: monster.master = caster level.objects.append(monster) if caster is player or player.can_see(caster) or player.can_see(monster): ui.message(util.capitalize(caster.get_name('summon{s}')) + ' ' + monster.get_name(determiner='a') + '.', rl.BLUE)
def control(self, actor): dx, dy = rl.random_int(-1, 1), rl.random_int(-1, 1) actor.set_action(actions.Move(actor, dx, dy))
def control(self, monster): old_target = monster.target old_flee = monster.flee def info(*args, **kwargs): pass #if debug.active: # if (player.can_see(monster) or old_flee is player or old_target is player): # print('AI', monster.name, *args, **kwargs) info(monster.info()) target = monster.target # dangling target after it becomes friendly # no dead target # 25% chance to forget target if monster can't see it if target \ and (not target.is_hostile(monster) \ or target.hp <= 0 \ or (not monster.can_see(target) and rl.random() < .25)): target = monster.target = None info(' forget target') # save target last seen location to be able to follow it when it leaves fov if target: monster.target_location = (target.x, target.y) info(' save target location %d,%d' % (target.x, target.y)) else: monster.target_location = None flee = monster.flee # dangling flee after it becomes friendly # no dead flee # 25% chance to forget flee if monster can't see it if flee \ and (not flee.is_hostile(monster) \ or flee.hp <= 0 \ or (not monster.can_see(flee) and rl.random() < .25)): flee = monster.flee = None info(' forget flee') # compute flee based on friends and foes friends, foes = util.friends_and_foes(monster) friends += [monster] if len(foes) > 0: #foes_hp = sum([m.hp for m in foes]) friends_hp = sum([m.hp for m in friends]) foes_power = sum([m.power for m in foes]) #friends_power = sum([m.power for m in friends]) #foes_defense = sum([m.defense for m in foes]) friends_defense = sum([m.defense for m in friends]) if foes_power - friends_defense >= friends_hp: flee = monster.flee = util.closest_hostile( monster, monster.sight_radius) info(' outnumbered, flee', str(flee)) # highest priority is to flee if flee and monster.can_see(flee): monster.move_away_from(flee.x, flee.y) info(' flee: move away from', str(flee)) elif not target: # if no target, move towards master or select new target if monster.master and monster.distance_to(monster.master) > 3: monster.move_towards(monster.master.x, monster.master.y) info(' no target: move towards master') else: target = monster.target = util.closest_hostile( monster, monster.sight_radius) info(' new target: ', str(target)) else: if monster.can_see(target): if monster.distance_to(target) >= 2: potential_actions = [ action for action in monster.actions if monster.mana >= action.cost ] info('potential-actions', monster.mana, potential_actions) value = rl.random() info(value, value < .5, value > .5) if len(potential_actions) > 0 and value < .5: selected = rl.random_int(0, len(monster.actions) - 1) action = monster.actions[selected] info('selected', action) if monster.distance_to(target) <= action.range: if action in [ powers.LIGHTNING, powers.FEAR, powers.SUMMON, powers.SUMMON_BAT, powers.SUMMON_EYE, powers.SUMMON_RAT, powers.SUMMON_SKELETON, powers.SUMMON ]: action.perform(monster) elif action in [ powers.CONFUSE, powers.FREEZE, powers.TELEPORT ]: action.perform(monster, target) elif action in [powers.FIREBALL, powers.DIG]: action.perform(monster, target.x, target.y) else: raise ValueError( 'monster cannot handle action ' + str(action)) info(' perform action: ', str(action)) else: monster.move_towards(target.x, target.y) info(' move towards target: ', str(target)) else: monster.move_towards(target.x, target.y) info(' move towards target: ', str(target)) else: monster.attack(target) info(' move towards target: ', str(target)) elif monster.target_location is not None and not ( monster.x == monster.target_location[0] and monster.y == monster.target_location[1]): monster.move_towards(monster.target_location[0], monster.target_location[1]) info(' move towards old target location %d,%d' % monster.target_location) elif target is not None: if powers.DIG in monster.actions: powers.DIG.perform(monster, x=target.x, y=target.y) info(' dig to reach target at %d,%d' % (target.x, target.y)) else: info(' wait, no dig') else: info(' wait') info(' <<', monster.info()) if monster.action is None: monster.wait()
def choose_template(templates): return templates[rl.random_int(0, len(templates) - 1)]
def apply_transform(template): transform = transforms[rl.random_int(0, len(transforms) - 1)] return transform(template)
def make_dungeon_map(): level = Level(const.MAP_WIDTH, const.MAP_HEIGHT) level.tiles.fill(Tile.WALL) level.blocked.fill(1) level.objects = [player] rooms = [] num_rooms = 0 for r in range(const.MAX_ROOMS): if num_rooms == 0: w = rl.random_int(const.ROOM_MIN_SIZE - 1, const.ROOM_MIN_SIZE + 2) h = rl.random_int(const.ROOM_MIN_SIZE - 1, const.ROOM_MIN_SIZE + 2) else: w = rl.random_int(const.ROOM_MIN_SIZE, const.ROOM_MAX_SIZE) h = rl.random_int(const.ROOM_MIN_SIZE, const.ROOM_MAX_SIZE) x = rl.random_int(0, level.width - w - 1) y = rl.random_int(0, level.height - h - 1) new_room = Rect(x, y, w, h) failed = False for other_room in rooms: if new_room.intersect(other_room): failed = True break if not failed: level.create_room(new_room) (new_x, new_y) = new_room.center() if num_rooms == 0: player.x = new_x player.y = new_y else: (prev_x, prev_y) = rooms[num_rooms - 1].center() if rl.random_int(0, 1) == 1: level.create_h_tunnel(prev_x, new_x, prev_y) level.create_v_tunnel(prev_y, new_y, new_x) else: level.create_v_tunnel(prev_y, new_y, prev_x) level.create_h_tunnel(prev_x, new_x, new_y) monsters.place_objects(level, new_room) rooms.append(new_room) num_rooms += 1 if game.dungeon_level == 3: level.create_river(Tile.WATER) elif game.dungeon_level == 5: level.create_river(Tile.LAVA) level.stairs = actors.Actor(new_x, new_y, graphics.stairs_for_level[game.dungeon_level], 'stairs', rl.WHITE, always_visible=True, z=-2) level.objects.append(level.stairs) level.compute_fov() return level