def check_level_up(): # see if the player's experience is enough to level-up level_up_xp = g.LEVEL_UP_BASE + (player.level - 1) * g.LEVEL_UP_FACTOR if player.fighter.xp >= level_up_xp: # it is! level up player.level += 1 player.fighter.xp -= level_up_xp g.message( 'Your battle skills grow stronger! You reached level ' + str(player.level) + '!', libtcod.yellow) choice = None while choice is None: # keep asking until a choice is made choice = menu('Level up! Choose a stat to raise:\n', [ 'Constitution (+20 HP, from ' + str(player.fighter.max_hp(player)) + ')', 'Strength (+1 attack, from ' + str(player.fighter.power(player)) + ')', 'Agility (+1 defense, from ' + str(player.fighter.defense(player)) + ')' ], g.LEVEL_SCREEN_WIDTH) if choice == 0: player.fighter.base_max_hp += 20 player.fighter.hp += 20 elif choice == 1: player.fighter.base_power += 1 elif choice == 2: player.fighter.base_defense += 1
def player_death(player, objects): # the game ended! g.message('You died!', libtcod.red) g.game_state = 'dead' # for added effect, transform the player into a corpse! player.char = '%' player.color = libtcod.dark_red
def equip(self): # if the slot is already being used, dequip whatever is there first old_equipment = get_equipped_in_slot(self.slot, g.inventory) if old_equipment is not None: old_equipment.dequip() # equip object and show a message about it self.is_equipped = True g.message('Equipped ' + self.owner.name + ' on ' + self.slot + '.', libtcod.light_green)
def cast_heal(): # heal the player if player.fighter.hp == player.fighter.max_hp(player): g.message('You are already at full health.', libtcod.red) return 'cancelled' g.message('Your wounds start to feel better!', libtcod.light_violet) player.fighter.heal(g.HEAL_AMOUNT, player)
def take_turn(self, fov_map, player, map, objects): if self.num_turns > 0: # still confused... # move in a random direction, and decrease the number of turns confused self.owner.move(libtcod.random_get_int(0, -1, 1), libtcod.random_get_int(0, -1, 1), map, objects) self.num_turns -= 1 else: # restore the previous AI (this one will be deleted because it's not referenced anymore) self.owner.ai = self.old_ai g.message('The ' + self.owner.name + ' is no longer confused!', libtcod.red)
def attack(self, target, objects, player): # a simple formula for attack damage damage = self.power(player) - target.fighter.defense(player) if damage > 0: # make the target take some damage g.message(self.owner.name.capitalize() + ' attacks ' + target.name + ' for ' + str(damage) + ' hit points.') target.fighter.take_damage(damage, objects, player) else: g.message(self.owner.name.capitalize() + ' attacks ' + target.name + ' but it has no effect!')
def drop(self, player, objects): # add to the map and remove from the player's inventory. also, place it at the player's coordinates objects.append(self.owner) g.inventory.remove(self.owner) self.owner.x = player.x self.owner.y = player.y # special case: if the object has the Equipment component, dequip it before dropping if self.owner.equipment: self.owner.equipment.dequip() g.message('You dropped a ' + self.owner.name + '.', libtcod.yellow)
def use(self): # special case: if the object has the Equipment component, the "use" action is to equip/dequip if self.owner.equipment: self.owner.equipment.toggle_equip() return # just call the "use_function" if it is defined if self.use_function is None: g.message('The ' + self.owner.name + ' cannot be used.') else: if self.use_function() != 'cancelled': g.inventory.remove(self.owner) # destroy after use, unless it was cancelled for some reason
def pick_up(self, objects): # add to the player's inventory and remove from the map if len(g.inventory) >= 26: g.message('Your inventory is full, cannot pick up ' + self.owner.name + '.', libtcod.red) else: g.inventory.append(self.owner) objects.remove(self.owner) g.message('You picked up a ' + self.owner.name + '!', libtcod.green) # special case: automatically equip, if the corresponding equipment slot is unused equipment = self.owner.equipment if equipment and get_equipped_in_slot(equipment.slot, g.inventory) is None: equipment.equip()
def monster_death(monster, objects): # transform it into a nasty corpse! it doesn't block, can't be # attacked and doesn't move g.message('The ' + monster.name + ' is dead! You gain ' + str(monster.fighter.xp) + ' experience points.', libtcod.orange) g.message(monster.name.capitalize() + ' is dead!') monster.char = '%' monster.color = libtcod.dark_red monster.blocks = False monster.fighter = None monster.ai = None monster.name = 'remains of ' + monster.name monster.send_to_back(objects)
def cast_lightning(): # find closest enemy (inside a maximum range) and damage it monster = closest_monster(g.LIGHTNING_RANGE) if monster is None: # no enemy found within maximum range g.message('No enemy is close enough to strike.', libtcod.red) return 'cancelled' # zap it! g.message( 'A lighting bolt strikes the ' + monster.name + ' with a loud thunder! The damage is ' + str(g.LIGHTNING_DAMAGE) + ' hit points.', libtcod.light_blue) monster.fighter.take_damage(g.LIGHTNING_DAMAGE, objects, player)
def next_level(): global dungeon_level # advance to the next level g.message('You take a moment to rest, and recover your strength.', libtcod.light_violet) player.fighter.heal(player.fighter.max_hp(player) / 2, player) # heal the player by 50% g.message( 'After a rare moment of peace, you descend deeper into the heart of the dungeon...', libtcod.red) make_map() # create a fresh new level! initialize_fov() dungeon_level += 1
def cast_confuse(): # ask the player for a target to confuse g.message('Left-click an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan) monster = target_monster(g.CONFUSE_RANGE) if monster is None: return 'cancelled' # replace the monster's AI with a "confused" one; after some turns it will restore the old AI old_ai = monster.ai monster.ai = o.ConfusedMonster(old_ai) monster.ai.owner = monster # tell the new component who owns it g.message( 'The eyes of the ' + monster.name + ' look vacant, as he starts to stumble around!', libtcod.light_green)
def new_game(): global player, con # create object representing the player fighter_component = o.Fighter(hp=30, defense=2, power=5, death_function=o.player_death, xp=0) player = o.Object(0, 0, '@', 'player', libtcod.white, blocks=True, fighter=fighter_component) player.level = 1 # generate map (at this point it's not drawn to the screen) make_map() initialize_fov() g.game_state = 'playing' g.inventory = [] # create the list of game messages and their colors, starts empty g.game_msgs = [] # initial equipment: a dagger equipment_component = o.Equipment(slot='right hand', power_bonus=2) obj = o.Object(0, 0, '-', 'dagger', libtcod.sky, equipment=equipment_component) g.inventory.append(obj) equipment_component.equip() obj.always_visible = True # a warm welcoming message! g.message('Welcome stranger! Prepare to die.', libtcod.red)
def cast_fireball(): global objects, player # ask the player for a target tile to throw a fireball at g.message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan) (x, y) = target_tile() if x is None: return 'cancelled' g.message( 'The fireball explodes, burning everything within ' + str(g.FIREBALL_RADIUS) + ' tiles!', libtcod.orange) for obj in objects: # damage every fighter in range, including the player if obj.distance(x, y) <= g.FIREBALL_RADIUS and obj.fighter: g.message( 'The ' + obj.name + ' gets burned for ' + str(g.FIREBALL_DAMAGE) + ' hit points.', libtcod.orange) obj.fighter.take_damage(g.FIREBALL_DAMAGE, objects, player)
def dequip(self): # dequip object and show a message about it if not self.is_equipped: return self.is_equipped = False g.message('Dequipped ' + self.owner.name + ' from ' + self.slot + '.', libtcod.light_yellow)