def distribute_exp():
    """ Open a menu in which the player can distribute the EXP he acquired."""
    from render import menu, message
    options = [
        'Strength (500)', 'Dexterity (500)', 'Stamina (500)',
        '1 -0 Health Level (500)', '1 -1 Health Level (400)',
        '1 -2 Health Level (300)', '1 -4 Health Level (200)'
    ]
    choice = menu('Choose a skill point to distribute points to:',
                  options,
                  50,
                  alpha=1)
    cost = {0: 500, 1: 500, 2: 500, 3: 500, 4: 500, 5: 400, 6: 300, 7: 200}
    if choice is not None:
        if choice == 'exit':
            return
        if gvar.game.player.exp >= cost[choice]:
            skill = {
                0: gain_strength,
                1: gain_dexterity,
                2: gain_stamina,
                3: gain_perception,
                4: gain_0hl,
                5: gain_1hl,
                6: gain_2hl,
                7: gain_4hl
            }
            amount = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1}
            skill[choice](amount[choice])
            gvar.game.player.exp -= cost[choice]
            message('You feel stronger!')
        else:
            message('You don\'t have enough experience.')
	def dequip(self):
		""" Dequips the Equipment"""
		from render import message

		if not self.is_equipped: return
		self.is_equipped = False
		message('Put off the ' + self.owner.name, libtcod.yellow)
	def ranged_attack():
		""" |  Perform a ranged attack.
			|  Checks for a ranged weapon in hand and then calls *input.target_tile* to determine a target.
		"""
		from input import target_tile
		from render import message
		from utils import is_player

		mobToHit = None
		if self.owner.get_equipped_in_slot('hand').ranged is not None:
			if is_player(self.owner):
				target = target_tile()
				if target == 'cancelled':
					return 'cancelled'
			else:
				target = 'closest'
			if target == 'closest':
				mobToHit = closest_mob(self.perception + self.skills['Awareness']*2)
			else:
				for hit in self.currentmap().objects:
					if target.x == hit.x and target.y == hit.y and hit.fighter is not None:
						mobToHit = hit

			if mobToHit is None:
				# @TODO No enemy found, drop arrow
				if is_player(self.owner):
					message('There is no enemy here!', libtcod.orange)
			else:
				self.attack(mobToHit.fighter, ranged=True)
				return self.owner.get_equipped_in_slot('hand').ranged.speed
		else:
			if is_player(self.owner):
				message('You do not have a ranged weapon equipped!', libtcod.orange)
		return 'cancelled'
def player_death(player):
    """ |  Death function for the player.
            |  Set game state to 'dead' and turn the player into a corpse.
    """
    from render import message
    message('You died!', libtcod.red)
    gvar.game.game_state = 'dead'
    gvar.game.player.char = '%'
    gvar.game.player.color = libtcod.dark_red
	def take_turn(self):
		""" |  Normal Turn Routine
			|  Mobs take turns if the player can hear it #TODO Change to Mob's FOH
			|  If they're close enough, they attack
		"""
		from render import message
		from utils import a_star_search, applyBonus, revertBonus

		mob = self.owner
		status = self.status
		delta = self.statusDelta

		speed = 1	# Base speed of 1 tick, to prevent infinite mob action loops

		if libtcod.map_is_in_fov(gvar.foh_map, mob.x, mob.y):

			# Normal Behaviour
			if not status:
				if mob.distance_to(gvar.game.player) >= 2:	# Move towards the player, if she's too far away
					destination = a_star_search(gvar.game.player.currentmap(), (mob.x, mob.y),(gvar.game.player.x, gvar.game.player.y)) # Perform A*-Search

					if destination == [(mob.x, mob.y), (mob.x, mob.y)]:	# idle for the next 3 turns, if no path to player is found
						self.status = 'idle'							#@TODO Should have prevented A*-Overload, doesn't seem to work
						self.statusDelta = 3
					mob.move_to(destination[-2])
					speed = self.owner.fighter.movement_speed()

				elif sum(gvar.game.player.fighter.hl) > 0:				# Player is close enough, perform an attack
					speed = mob.fighter.attack(gvar.game.player.fighter)


			# Confusion - Moves to random directions, also -4 dodgeDV penalty
			elif status == 'confusion':
				applyBonus('dodgeDV', -4, self.owner.fighter)
				if delta > 0:
					self.owner.move(libtcod.random_get_int(0, -1, 1), libtcod.random_get_int(0, -1, 1))
					self.statusDelta -= 1
				else:
					revertBonus('dodgeDV', -4, self.owner.fighter)
					self.status = None
					message('The smoke around the ' + self.owner.name + ' disappears.', libtcod.violet)
				if self.owner.fighter is not None:
					speed = self.owner.fighter.movement_speed()


			# Idle - Don't do anything, Speed 3
			elif status == 'idle':
				if delta > 0:
					self.statusDelta -= 1
				else:
					self.status = None
				speed = 3

		return speed
	def equip(self):
		""" |  Equips the Equipment
			|  Dequipping other items in this slot
		"""
		from render import message

		currentEquipped = gvar.game.player.get_equipped_in_slot(self.slot)
		if currentEquipped is not None:
			currentEquipped.dequip()
		self.is_equipped = True
		message('Equipped the ' + self.owner.name, libtcod.yellow)
def cast_heal(min, max, mutableBySkill=True):
    """ |  **Spells and Effects: Heal player**
            |  Heal the player by a random value between *min* and *max* boundaries.
            |  If *mutableBySkill* is true, the player's Medicine skill adds to the boundaries.
    """
    from render import message

    message('You start to feel better!', libtcod.light_violet)
    modifier = 0
    if mutableBySkill:
        modifier = libtcod.random_get_int(
            0, 0, gvar.game.player.fighter.skills['Medicine'])
    gvar.game.player.fighter.heal(
        libtcod.random_get_int(0, min, max) + modifier)
	def drop(self):
		""" |  Drop the Item
			|  Add it to the Global Objects List
			|  and set it's coordinates to the dropper's
		"""
		from render import message

		if self.owner.equipment:
			self.owner.equipment.dequip()
		gvar.game.player.currentmap().objects.append(self.owner)
		gvar.game.player.inventory.remove(self.owner)
		self.owner.currentDungeon = gvar.game.player.currentmap().owner
		self.owner.currentLevel = gvar.game.player.currentmap().owner.maps.index(gvar.game.player.currentmap())-1
		self.owner.x = gvar.game.player.x
		self.owner.y = gvar.game.player.y
		message('You dropped a ' + self.owner.name + '.', libtcod.yellow)
def mob_death(mob):
    """ |  Death function for mobs.
            |  Turns the mob into a corpse. It doesn't block, has no AI and Fighter components.
            |  Also, add the mob's EXP value to the player's.
    """
    from render import message
    message(mob.name.capitalize() + ' is dead!', libtcod.orange)
    mob.char = '%'
    mob.color = libtcod.dark_red
    mob.blocks = False
    mob.fighter = None
    mob.ai = None
    mob.name = 'remains of ' + mob.name
    mob.send_to_back()
    gvar.game.player.exp += mob.exp
    message('Gained ' + str(mob.exp) + ' EXP!')
def cast_lightning(min, max):
    """ |  **Spells and Effects: Lightning**
            |  Cast a lightning and deal a random amount of Lethal damage inside the given *min* and *max* boundaries.
            |  The target is the closest mob inside a radius of 5 fields.
    """
    from utils import closest_mob
    from render import message

    mob = closest_mob(5)
    if mob is None:
        message('No enemy is close enough to strike.', libtcod.red)
        return 'cancelled'
    damage = libtcod.random_get_int(0, min, max)
    message(
        'A lighting bolt strikes the ' + mob.name + '! The damage is ' +
        str(damage) + ' Lethal HL.', libtcod.light_blue)
    mob.fighter.take_damage(damage)
def cast_confusion(min, max):
    """ |  **Spells and Effects: Confusion**
            |  Apply a 'confusion' status to the closes mob within 5 fields.
            |  A confused enemy moves into a random direction every time it takes a turn.
            |  Also, it doesn't react to ai_blocked tiles, so it can fall down a hole.
    """
    from utils import closest_mob
    from render import message

    mob = closest_mob(5)
    if mob is None:
        message('No enemy is close enough to target.', libtcod.red)
        return 'cancelled'

    message(
        'A violet smoke appears around the ' + mob.name + '! It\'s confused.',
        libtcod.violet)
    mob.ai.applyStatus('confusion', libtcod.random_get_int(0, min, max))
def new_game():
	""" |  Start a new game
		|  Create a map, FOV Map and print Welcome Mesage
	"""
	from charactercreation import create_character
	from spawn import spawn_player

	gvar.game = gvar.Game()
	gvar.game.init_world()
	spawn_player([gvar.game.world.worldspace, 0])

	initialize_fov()
	color = libtcod.color_lerp(libtcod.white, libtcod.red, 0.9)
	render.message('Welcome, Child of the Unconquered Sun.', color)
	render.animate_background('main_menu', 0.5, reverse=True)
	ch = create_character()
	if ch == 'exit':
		main_menu()
	play_game()
def new_game():
    """ |  Start a new game
		|  Create a map, FOV Map and print Welcome Mesage
	"""
    from charactercreation import create_character
    from spawn import spawn_player

    gvar.game = gvar.Game()
    gvar.game.init_world()
    spawn_player([gvar.game.world.worldspace, 0])

    initialize_fov()
    color = libtcod.color_lerp(libtcod.white, libtcod.red, 0.9)
    render.message('Welcome, Child of the Unconquered Sun.', color)
    render.animate_background('main_menu', 0.5, reverse=True)
    ch = create_character()
    if ch == 'exit':
        main_menu()
    play_game()
def cast_fireball(min, max, explosion_radius=2):
    """ |  **Spells and Effects: Fireball.**
            |  Target a tile within the FOV with and cast a fireball with given explosion *radius* onto the targeted tile.
            |  Deal damage equal to a random value inside the *min* and *max* boundaries.
    """
    from input import target_tile
    from render import message

    target = target_tile(radius=explosion_radius)
    if target == 'cancelled':
        return 'cancelled'
    if target is not None:
        for hit in gvar.game.objects:
            for p in target.splash.circle:
                if hit.fighter is not None:
                    if hit.fighter.hl[3] > 0:
                        if hit.x == p[0] and hit.y == p[1]:
                            dmg = libtcod.random_get_int(0, min, max)
                            hit.fighter.take_damage(dmg)
                            message('The fireball burns ' + hit.name +
                                    ' for ' + dmg + ' damage')
	def use(self):
		""" |  call the use_function, if its defined
			|  if there are args, add them to the call
		"""
		from render import message

		if self.use_function is None and self.owner.equipment is None: 		# No valid action found
			message('The ' + self.owner.name + ' cannot be used.')
		elif self.use_function is None and self.owner.equipment is not None:# It's an Equipment, equip it
			self.owner.equipment.toggle_equip()
		else:
			if len(self.args) == 0:
				if self.use_function() != 'cancelled':						# It can be used, call use_function
					if self.count>=2:										# Decrease Stack count, or remove the item
						self.count -= 1
					else:
						gvar.game.player.inventory.remove(self.owner)		# @TODO Inventory for Mobs
			else:
				if self.use_function(self.args[0], self.args[1]) != 'cancelled':
					if self.count>=2:
						self.count -= 1
					else:
						gvar.game.player.inventory.remove(self.owner)
	def attack(self, target, ranged=False):
		""" |  Performs an attack against the target.
			|  Melee or ranged indicated by ranged boolean
		"""
		from utils import d10, is_player
		from render import message

		activeWeapon = self.owner.get_equipped_in_slot('hand')
		if (activeWeapon is None):										# get equipped weapon from slot 'hand'
			activeWeapon = Equipment(slot='hand', weapon=Weapon())  	# Insert dummy weapon if no weapon is equipped
																		# @TODO Two-Handed Weapons

		weapon = activeWeapon.ranged if ranged else activeWeapon.weapon 				# determine weapon/ranged component to use

		hitroll = d10(self.dexterity + self.skills[weapon.skill], 'Hit', player=is_player(self.owner))
		if hitroll[0] != 'botch':
			hitroll[0] -= self.health_penalty()
			hitroll[0] = max(hitroll[0], 0)
		else:
			pass	# @TODO Botch Behaviour

		# Check for hit, if successes > enemy's dodge DV ( -onslaughtPenalty for player)
		threshold = target.dodgeDV()
		threshold -= target.onslaughtPenalty() if is_player(target.owner) else 0

		if hitroll[0] != 'botch' and hitroll[0] > threshold:	# target hit, roll damage
			if ranged:
				dmgroll = d10(weapon.damage, 'Damage', botchable=False, player=is_player(self.owner))
			else:
				dmgroll = d10(self.strength + weapon.damage, 'Damage', botchable=False, player=is_player(self.owner))
			damage = dmgroll[0] + (hitroll[0] - threshold)		# add remaining successes from hitroll

			if damage > target.hardness():						# check if damage is bigger than hardness
				if weapon.damage_type == 'bashing':				# apply soak
					damage -= target.bashing_soak()
				elif weapon.damage_type == 'lethal':
					damage -= target.lethal_soak()

				if damage > 0:									# apply damage
					message(self.owner.name.capitalize() + ' hits ' + target.owner.name + ' for ' + str(damage) + ' hit points.')
					target.take_damage(damage)
				else:
					message(self.owner.name.capitalize() + ' hits ' + target.owner.name + ' but the strike bounces off his armor!')
		else:
			message(self.owner.name.capitalize() + ' attacks ' + target.owner.name + ' but misses.')

		return weapon.speed
	def pick_up(self):
		""" |  Add the item to the player's inventory
			|  @TODO Inventory System for Enemies and other NPCs
		"""
		from render import message

		if len(gvar.game.player.inventory) >= 26:	# Inventory Full, Alphabetical Limit
													# @TODO Multi-Page Menus & Encumbrance System
			message('Your inventory is full, cannot pick up ' + self.owner.name + '.', libtcod.red)
		else:
			for item in gvar.game.player.inventory:	# Loop through items already in inventory
				if item.name == self.owner.name:	# and stack them
					item.item.count += 1
					gvar.game.player.currentmap().objects.remove(self.owner)	# Remove the item from the map
					message('You picked up a ' + self.owner.name + '!', libtcod.green)
					return
			gvar.game.player.inventory.append(self.owner)
			gvar.game.player.currentmap().objects.remove(self.owner)
			message('You picked up a ' + self.owner.name + '!', libtcod.green)
def fall_into(entity):
    """ |  Step function:
            |  Fall into a hole. Takes an entity (player, mob) as input.
            |  Rolls Wits + Awareness to determine, if they see the hole.
            |  If they do and there is a ledge near (not diagonally), do a reflexive Wits+Awareness roll, to determine if they can hang down the ledge.
            |  Otherwise, kill them.
    """
    from utils import d10, is_player
    from render import message

    map = entity.currentmap()
    result = d10(entity.fighter.wits + entity.fighter.skills["Awareness"],
                 'Fall:',
                 botchable=True,
                 player=is_player(entity))
    if result[0] <= 3:
        ledge = False
        for spot in map.adjacent(entity.x, entity.y, diagonal=False):
            if map.map[spot[0]][spot[1]].tile_type == 'floor':
                ledge = True
        if ledge:
            # ledge near, do a reflex roll to catch it
            reflex = d10(entity.fighter.wits +
                         entity.fighter.skills["Awareness"],
                         'Reflex:',
                         botchable=True,
                         player=is_player(entity))
            if reflex[0] >= 2:
                if not is_player:
                    message(entity.name + " is hanging down the ledge",
                            libtcod.white)
                else:
                    message("You are hanging down the ledge", libtcod.white)
            else:
                if not is_player:
                    message(entity.name + " has fallen down the hole",
                            libtcod.red)
                else:
                    message("You have fallen down the hole", libtcod.red)
                    entity.fighter.death_function(entity)
        else:
            # no ledge near
            if not is_player:
                message(entity.name + " has fallen down the hole", libtcod.red)
            else:
                message("You have fallen down the hole", libtcod.red)
            entity.fighter.death_function(entity)
    else:
        if not is_player:
            message(entity.name + " is hanging down the ledge", libtcod.white)
        else:
            message("You are hanging down the ledge", libtcod.white)