def check_level_up(): #see if the player's experience is enough to level-up level_up_xp = defn.LEVEL_UP_BASE + defn.player.properties[ 'level'] * defn.LEVEL_UP_FACTOR if defn.player.creature.xp >= level_up_xp: #it is! level up defn.player.properties['level'] += 1 defn.player.creature.xp -= level_up_xp gui.message( 'You reached level ' + str(defn.player.properties['level']) + '!', libtcod.yellow) choice = None while choice == None: #keep asking until a choice is made choice = gui.menu('Level up! Choose a stat to raise:\n', [ '+4 Life, from ' + str(defn.player.creature.base_max_hp), '+5 Mana Capacity, from ' + str(defn.player.creature.base_max_mana), '+1 Channeling, from ' + str(defn.player.creature.channeling) ], defn.LEVEL_SCREEN_WIDTH) if choice == 0: defn.player.creature.base_max_hp += 4 defn.player.creature.hp += 4 elif choice == 1: defn.player.creature.base_max_mana += 5 defn.player.creature.mana += 5 elif choice == 2: defn.player.creature.channeling += 1
def target_monster(max_range=None): gui.message('Left-click on target, or right-click to cancel.', libtcod.light_cyan) #primitive function highlighting range. Ideally would be implemented in read-only attribute of tile rangemap = defn.fov_map libtcod.map_compute_fov(rangemap, defn.player.x, defn.player.y, max_range, defn.FOV_LIGHT_WALLS, defn.FOV_ALGO) #returns a clicked monster inside FOV up to a range, or None if right-clicked while True: (x, y) = target_tile(max_range) if x is None: #player cancelled #remove highlight for y in range(defn.MAP_HEIGHT): for x in range(defn.MAP_WIDTH): if libtcod.map_is_in_fov(rangemap, x, y): libtcod.console_set_char_background(defn.con, x, y, defn.dungeon[x][y].color, libtcod.BKGND_SET) return None #return the first clicked creature, otherwise continue looping for obj in defn.dungeon[x][y].objects: if obj.creature: return obj
def cast_heal(parameters): #heal the player if defn.player.creature.hp == defn.player.creature.max_hp: gui.message('You are already at full health.', libtcod.red) return 'cancelled' gui.message('Your wounds start to feel better!', libtcod.light_violet) defn.player.creature.heal(parameters['amount healed'])
def new_game(): #create object representing the player object.make_monster(0, 0, 'player', monst.properties['player']) cfg.player.level = 1 #generate map (at this point it's not drawn to the screen) cfg.dungeon_level = 1 mapgen.make_bsp() mapgen.initialize_fov() cfg.game_state = 'playing' cfg.run_realtime = cfg.REAL_TIME cfg.inventory = [] #create the list of game messages and their colors, starts empty cfg.game_msgs = [] #create dictionaries of populations for monsters cfg.population = {} cfg.max_population = {} object.initialize_population() #a warm welcoming message! gui.message('Beginning genetic simulation.', libtcod.light_red * 0.7)
def onOk(self, evt): name = self.nameEdit.GetValue() if not name: gui.messageBox( # Translators: A message box telling the user that he must enter a theme name in order to continue _("Please enter a name for your theme package."), # Translators: The title of a message box indicating an error. _("Error")) return try: name.encode("mbcs") except UnicodeEncodeError: gui.message( # Translators: A message telling the user to change the name of the new theme because the entered name is not valid. _("The theme name you have entered is not valid. Please try another name." ), # Translators: The title of a message box indicating an error. _("Error")) self.nameEdit.Clear() return self.nameEdit.SetFocus() infoDict = { "name": name, # Translators: The default message in the audio themes manager dialog which will be shown when no author was set for the audio theme. "author": self.authorEdit.GetValue() or _("Not Set"), # Translators: The default message in the audio themes manager dialog which will be shown when no description was set for the audio theme "summary": self.descEdit.GetValue() or _("No Description") } super(CreaterDialog, self).onOk(evt) dg = CreateNewDialog(parent=gui.mainFrame, infoDict=infoDict) dg.CenterOnScreen() dg.Show()
def declare_attack(self, source, target, dice_bonus, d12_bonus): #to avoid the corpse-counterattack problem, check if target is attackable. if target.creature: if source.creature and 'daze' in source.creature.conditions and libtcod.random_get_int( 0, 1, 12) < 7: gui.message( source.title.capitalize() + ' is too dazed to attack!', libtcod.orange) source.creature.adjust_turn_counter(3) else: self.resolve_attack_roll(self.dice + dice_bonus, d12_bonus, source, target) #additional strikes do not gain any bonuses. if source.creature and (['doublestrike'] in self.traits or ['triplestrike']) in self.traits: self.resolve_attack_roll(self.dice, d12_bonus, source, target) if source.creature and ['triplestrike'] in self.traits: self.resolve_attack_roll(self.dice, d12_bonus, source, target) #remove daze (and potentially other conditions) at conclusion of attacks if source.creature: daze_removal = False for condition in source.creature.conditions: if condition == 'daze': source.creature.conditions.remove(condition) daze_removal = True if daze_removal: gui.message( source.title.capitalize() + ' is dazed no longer!', libtcod.orange)
def next_level(): #advance to the next level gui.message('You take a moment to rest, and recover your strength.', libtcod.light_violet) cfg.player.fighter.heal(cfg.player.fighter.max_hp / 2) #heal the player by 50% cfg.dungeon_level += 1 gui.message('After a rare moment of peace, you descend deeper into the heart of the dungeon...', libtcod.red) make_bsp() #create a fresh new level! initialize_fov()
def take_damage(self, amount): stats = self.owner.get_component("stats") if amount > 0: msg = "{} takes {} damage".format(self.owner.name, amount) stats.cur_hp -= amount if stats.cur_hp <= 0: self.die() else: msg = "{} shrugs the attack off".format(self.owner.name) gui.message(msg)
def organizeFiles(addrGUI): dest = addrGUI.get() addrGUI.delete(0, tk.END) if os.path.isdir(dest) == False: gui.message(False) return None allFiles = getOnlyFiles(dest) # Get list of files populate_filesAndExt(allFiles) # Populate Dictionary ext = identifyExt(dest, allFiles) populate_foldersToMake(ext) # Populate list with unique ext generateFolderDirectories(dest, foldersToMake) # Make new directories changePaths(dest, allFiles) gui.message(True)
def reset_global_variables(): #Reset all global variables to starting values defn.game_msgs = [] defn.inventory = [] defn.objects = [] defn.dungeon = [] defn.game = Game() defn.dungeon_level = 1 defn.game_state = 'playing' defn.player_location_changed = True defn.autoplaying = None gui.message ('Press *?* for a list of commands', libtcod.white)
def attack(self, target): destructible = target.get_component("destructible") if not destructible: return else: msg = "{} attacks {}".format(self.owner.name, target.name) gui.message(msg) #print "attack", "defense" #print self.attack_value, destructible.defense damage = max(self.attack_value - destructible.defense, 0) destructible.take_damage(damage)
def tame_animal(parameters, source=None, target=None): source=defn.player if parameters['target type']=='none': pass #eventually, maybe we can upgrade so that everything in sight is converted. elif parameters['target type']=='creature': #find target. currently geared to ranged attacks, though can easily be extended to melee target = spfn.target_monster(parameters['range']) if target: if target.creature.alignment == 'dungeon' and 'animal' in target.properties['subtypes']: if defn.player.properties['level'] > target.properties['level']: health_percentage = float(target.creature.hp) / float(target.creature.max_hp) if health_percentage <= parameters['threshold'] or target.creature.hp == 1: target.creature.alignment = 'player' target.color = libtcod.white gui.message('The ' + target.name + ' submits to your will!', libtcod.green) choice = gui.menu('Name your new friend?', ['Yes', 'No'], 24) if choice == 0: #give it a name name = itxt.input_text(50, 5, 'What would you like to name it?\n\n', '\n\n(Press *ENTER* when done)') target.personal_name = name return 'succeeded' else: gui.message('The ' + target.name + ' resists your attempts to tame it.', libtcod.red) return 'failed' gui.message('You are not experienced enough to tame that.', libtcod.white) return 'cancelled' gui.message('You cannot tame that!', libtcod.white) return 'cancelled' else: return 'cancelled'
def take_damage(self, damage): #apply damage if possible if damage > 0: self.hp -= damage #dig through wall if self.hp <= 0: self.blocked = False self.block_sight = False self.hp = self.max_hp gui.message('The wall crumbles!',libtcod.light_orange) #update map cfg.fov_recompute = True
def check_data_retrieval_error(price_data, dependent_var_list, independent_var_list, data_source): """Check whether there was an error retrieving data""" # Check whether data was retrieved successfully: tickers_list = dependent_var_list + independent_var_list if data_source == 'Bloomberg': if len(tickers_list) > 1: if len(price_data.columns) < len(tickers_list): und_errors = np.setdiff1d(tickers_list, price_data.columns) gu.message('There was a problem loading data for the following underlyings:\n' + '\n'.join(und_errors)) for und_error in und_errors: if und_error in dependent_var_list: dependent_var_list.remove(und_error) if und_error in independent_var_list: independent_var_list.remove(und_error) return dependent_var_list, independent_var_list
def next_level(): gui.message( 'You pass through the magical gate with some apprehension. The gate\'s magic heals and restores you.', libtcod.light_violet) #heal the player by 50% defn.player.creature.conditions = [] defn.player.creature.heal(defn.player.creature.max_hp) #store all friendly creatures for obj in defn.objects: if obj.creature and obj != defn.player and not obj in defn.player.creatures: if obj.creature.alignment == 'player': defn.player.creatures.append(obj) #increment the dungeon level defn.dungeon_level += 1 #generate a new map make_map() mpfn.initialize_fov()
def book(parameters): spellbook = defn.player.spellbook #lets you learn 1-6 copies of spell, depending on training and level. level_copies = [4,2,1,1,0,0] #this is not actually the spell instance that will go in your book - it is just a placeholder to find the properties spell = parameters['spell'] if spell.properties['school'] in defn.antitraining: level_copies = [2,1,0,0,0,0] if spell.properties['school'] in defn.training: level_copies = [6,4,2,2,1,1] if level_copies[spell.properties['level']-1] == 0: gui.message('Unfortunately, you are not skilled enough in the ' + spell.properties['school'] + ' school of magic to learn anything from this book.', libtcod.yellow) return 'cancelled' copies = level_copies[spell.properties['level']-1] if len(spellbook.contents)<26: spellbook.insert(spell, copies) gui.message('As you memorize the spells contained in the tome, it crumbles to dust.', libtcod.green) return 'succeeded' else: gui.message('Your spellbook is full.', libtcod.white) return 'cancelled'
def handle_keys(): #global keys if defn.key.vk == libtcod.KEY_ENTER and defn.key.lalt: #Alt+Enter: toggle fullscreen libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) elif defn.key.vk == libtcod.KEY_ESCAPE: return 'exit' #exit game if defn.game_state == 'playing': #autoplay module if defn.autoplaying != None: #if any hostile monsters are in view, stop autoplaying: for obj in defn.objects: if obj.creature and obj.creature.alignment != 'player' and libtcod.map_is_in_fov( defn.fov_map, obj.x, obj.y): gui.message( 'Seeing enemies, you decide to pay a bit more attention to what you are doing.', libtcod.white) defn.autoplaying = None break #Move. if defn.autoplaying == 'autoexplore': #first check if standing on an item. If so, pick it up. for obj in defn.dungeon[defn.player.x][ defn.player. y].objects: #look for an item in the player's tile if obj.item and len(defn.inventory) < 26: #we have an error where a full inventory will cause the player to remain on the same spot, continually trying to pick up the item. if obj.item.pick_up() == 'cancelled': defn.autoplaying == None return #compute fog of war (FOW) dijkstra map unexplored_tiles = [] for y in range(defn.MAP_HEIGHT): for x in range(defn.MAP_WIDTH): if not defn.dungeon[x][y].explored: unexplored_tiles.append(defn.dungeon[x][y]) #if inventory is not full, compute item map item_tiles = [] if len(defn.inventory) < 26: for obj in defn.objects: if obj.item and defn.dungeon[obj.x][obj.y].explored: item_tiles.append(defn.dungeon[obj.x][obj.y]) #check whether any target squares were found dijkstra_autoexplore_map = djks.Map(unexplored_tiles + item_tiles) dijkstra_autoexplore_map.compute_map() #if there is a next point, move there, otherwise cancel autoplay: if dijkstra_autoexplore_map.array[defn.player.x][ defn.player.y] < 999: destination = dijkstra_autoexplore_map.get_next_step( defn.player.x, defn.player.y) defn.player.creature.try_to_move(destination.x, destination.y) return else: #cancel autoplay; we're done defn.autoplaying = None elif defn.autoplaying == 'autoascend': #go up if standing on the portal if defn.stairs.x == defn.player.x and defn.stairs.y == defn.player.y: dgen.next_level() defn.autoplaying = None return #see if the portal is visible: portals = [] for y in range(defn.MAP_HEIGHT): for x in range(defn.MAP_WIDTH): if defn.stairs in defn.dungeon[x][ y].objects and defn.dungeon[x][y].explored: portals.append(defn.dungeon[x][y]) if portals: dijkstra_autoascend_map = djks.Map(portals) dijkstra_autoascend_map.compute_map() destination = dijkstra_autoascend_map.get_next_step( defn.player.x, defn.player.y) defn.player.creature.try_to_move(destination.x, destination.y) return #cancel autoplay; we're done defn.autoplaying = None else: #no portals found; cancel autoplay gui.message( 'You have no idea where the nearest portal is.', libtcod.white) defn.autoplaying = None else: #stop autoexploring when a key is pressed. defn.autoplaying = None #autofight with tab if defn.key.vk == libtcod.KEY_TAB: enemy_tiles = [] for obj in defn.objects: if obj.creature and obj.creature.alignment != 'player' and libtcod.map_is_in_fov( defn.fov_map, obj.x, obj.y): enemy_tiles.append(defn.dungeon[obj.x][obj.y]) if enemy_tiles: #create dijkstra map and roll towards nearest enemy. dijkstra_autofight_map = djks.Map(enemy_tiles) dijkstra_autofight_map.compute_map() destination = dijkstra_autofight_map.get_next_step( defn.player.x, defn.player.y) defn.player.creature.try_to_move(destination.x, destination.y) return else: gui.message('No enemies in sight!', libtcod.white) #cancel autoplay if any key is pressed if defn.key.vk != libtcod.KEY_NONE: defn.autoplaying = None #movement keys try: (dx0, dx1) = { libtcod.KEY_KP1: (-1, 1), libtcod.KEY_KP2: (0, 1), libtcod.KEY_KP3: (1, 1), libtcod.KEY_KP4: (-1, 0), libtcod.KEY_KP6: (1, 0), libtcod.KEY_KP7: (-1, -1), libtcod.KEY_KP8: (0, -1), libtcod.KEY_KP9: (1, -1) }[defn.key.vk] player_move_or_attack(dx0, dx1) except: if defn.key.vk == libtcod.KEY_KP5: return key_char = chr(defn.key.c) if key_char == ',': #pick up an item for obj in defn.dungeon[defn.player.x][ defn.player. y].objects: #look for an item in the player's tile if obj.item: obj.item.pick_up() return if key_char == '?': gui.msgbox( 'Use the numpad keys to move around. You can mouse-over any object to identify it. The following keys can be used to get more information:' + '\n\nI = examine an item in your inventory' + '\nZ = examine a spell in your spellbook' + '\nc = access information about your character' + '\nx = get information on a nearby object' + '\n\nThe following keys may be used to interact with your environment:' + '\n\nCOMMA = pick up item from current position' + '\ni = use an item from your inventory' + '\nd = drop an item from your inventory' + '\nz = choose a spell from your spellbook to cast' + '\ns = summon an ally from a previous level' + '\n\nTo avoid tedious repetition, you can automate certain tasks:' + '\n\n< = travel to the nearest portal and pass through' + '\no = autoexplore (press any key to stop exploring)' + '\nTAB = move towards/attack nearest enemy' + '\n\nYour objective on each level is to find the exit and ascend to the next level. Enemies will get more dangerous the further you go, so it will be to your advantage to pick up equipment that you find lying around.', 50) if key_char == 'i': #show the inventory; if an item is selected, use it chosen_item = inventory_menu( 'Press the key next to an item to use it, or any other to cancel.\n' ) if chosen_item is not None: if chosen_item.use() != 'cancelled': return if key_char == 'I': #show the inventory; if an item is selected, describe it chosen_item = inventory_menu( 'Press the key next to an item to examine it, or any other to cancel.\n' ) if chosen_item is not None: info = chosen_item.owner info.describe() if key_char == 'z': spellbook = defn.player.spellbook #show the spellbook; if a spell is selected, use it chosen_spell = spellbook_menu( 'Press the key next to a spell to cast it, or any other to cancel.\n' ) if chosen_spell is not None: if defn.player.creature.cast_spell( chosen_spell) != 'cancelled': spellbook.remove(chosen_spell, 1) return if key_char == 'Z': #show the inventory; if an item is selected, describe it chosen_spell = spellbook_menu( 'Press the key next to a spell for more information, or any other to cancel.\n' ) if chosen_spell is not None: chosen_spell.describe() if key_char == 'd': #show the inventory; if an item is selected, drop it chosen_item = inventory_menu( 'Press the key next to an item to drop it, or any other to cancel.\n' ) if chosen_item is not None: chosen_item.drop() chosen_item.owner.send_to_back() return chosen_item = inventory_menu( 'Press the key next to an item to drop it, or any other to cancel.\n' ) if chosen_item is not None: chosen_item.drop() return if key_char == '<': #go up if standing on the portal if defn.stairs.x == defn.player.x and defn.stairs.y == defn.player.y: dgen.next_level() defn.autoplaying = None #head toward portal and then blank input key in preparation for override. defn.autoplaying = 'autoascend' defn.key.vk = libtcod.KEY_NONE if key_char == 'o': #initialize autoexplore and then blank input key in preparation for override. defn.autoplaying = 'autoexplore' defn.key.vk = libtcod.KEY_NONE if key_char == 'c': #show character information #first, compute traits traits_inc = [] appended_traits = [] for trait in defn.player.traits: if trait[0] not in appended_traits: if len(trait) == 2: if trait[0][-1:] == '+': #sum them trait_inc = [ trait[0], data.sum_values_from_list( defn.player.traits, trait[0]) ] else: #find max trait_inc = [ trait[0], data.max_value_from_list( defn.player.traits, trait[0]) ] traits_inc.append(trait_inc) appended_traits.append(trait_inc[0]) else: traits_inc.append(trait) appended_traits.append(trait) traits = '' for trait in traits_inc: if len(trait) == 1: traits += '\n ' + trait[0].capitalize() else: traits += '\n ' + trait[0].capitalize() + str( trait[1]) #next, compute conditions conditions_inc = [] appended_conditions = [] for condition in defn.player.creature.conditions: if condition not in appended_conditions: conditions_inc.append([ condition, data.count_instances_in_list( defn.player.creature.conditions, condition) ]) appended_conditions.append(condition) conditions = '' for condition in conditions_inc: conditions += '\n ' + condition[0].capitalize( ) + ' (' + str(condition[1]) + ')' level_up_xp = defn.LEVEL_UP_BASE + defn.player.properties[ 'level'] * defn.LEVEL_UP_FACTOR gui.msgbox( 'Character Information\n\nLevel: ' + str(defn.player.properties['level']) + '\nExperience: ' + str(defn.player.creature.xp) + '\nExperience to level up: ' + str(level_up_xp) + '\n\nLife: ' + str(defn.player.creature.max_hp) + '\nMana Capacity: ' + str(defn.player.creature.max_mana) + '\n\nAttack: ' + str(defn.player.creature.active_attack.name.capitalize()) + '\n\nTraits:' + traits + '\n\nConditions' + conditions, defn.CHARACTER_SCREEN_WIDTH) if key_char == 'x': #first, select a target object target = None gui.message( 'Click on the object you would like to know more about, or right click to cancel.', libtcod.white) rangemap = defn.fov_map while True: #render the screen. this erases the inventory and shows the names of objects under the mouse. libtcod.console_flush() libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, defn.key, defn.mouse) game.render_all() (x, y) = (defn.mouse.cx, defn.mouse.cy) if defn.mouse.lbutton_pressed and libtcod.map_is_in_fov( defn.fov_map, x, y): for j in range(defn.MAP_HEIGHT): for i in range(defn.MAP_WIDTH): if libtcod.map_is_in_fov(rangemap, i, j): libtcod.console_set_char_background( defn.con, i, j, defn.dungeon[i][j].color, libtcod.BKGND_SET) break if defn.mouse.rbutton_pressed or defn.key.vk == libtcod.KEY_ESCAPE: for j in range(defn.MAP_HEIGHT): for i in range(defn.MAP_WIDTH): if libtcod.map_is_in_fov(rangemap, i, j): libtcod.console_set_char_background( defn.con, i, j, defn.dungeon[i][j].color, libtcod.BKGND_SET) break if x != None: for obj in defn.dungeon[x][y].objects: target = obj #describe the target more fully if it is a creature if target.creature: traits_inc = [] appended_traits = [] for trait in target.traits: if trait[0] not in appended_traits: if len(trait) == 2: if trait[0][-1:] == '+': #sum them trait_inc = [ trait[0], data.sum_values_from_list( target.traits, trait[0]) ] else: #find max trait_inc = [ trait[0], data.max_value_from_list( target.traits, trait[0]) ] traits_inc.append(trait_inc) appended_traits.append(trait_inc[0]) else: traits_inc.append(trait) appended_traits.append(trait) traits = '' for trait in traits_inc: if len(trait) == 1: traits += '\n ' + trait[0].capitalize() else: traits += '\n ' + trait[0].capitalize( ) + str(trait[1]) #next, compute conditions conditions_inc = [] appended_conditions = [] for condition in target.creature.conditions: if condition not in appended_conditions: conditions_inc.append([ condition, data.count_instances_in_list( target.creature.conditions, condition) ]) appended_conditions.append(condition) conditions = '' for condition in conditions_inc: conditions += '\n ' + condition[ 0].capitalize() + ' (' + str( condition[1]) + ')' title = target.title.capitalize() if target.title == target.name: title = title + ', ' + target.properties['name'] #now describe the creature gui.msgbox( title + '\n\nLevel: ' + str(target.properties['level']) + '\n\nAttack: ' + str(target.creature.active_attack.name. capitalize()) + '\n\nTraits:' + traits + '\n\nConditions:' + conditions + '\n\n' + target.properties['description'], defn.CHARACTER_SCREEN_WIDTH) else: target.describe() break #later, can add menu in case of multiple objects if key_char == 's': #summon an ally creature = summoning_menu( 'Press the key next to a creature to select it, or any other to cancel.\n' ) if creature != None: gui.message( 'Left-click on an open tile to summon creature there, or right-click to cancel.', libtcod.light_cyan) x0, x1 = spfn.target_tile(3) if mpop.place_object(creature, [x0, x1]) != 'failed': defn.player.creatures.remove(creature) else: gui.message('There\'s something in the way...', libtcod.light_cyan) #if key_char == 'w': #defn.player.creature.heal(30) #dgen.next_level() #defn.inventory.append(idic.get_item('scroll of animate dead',0,0)) #defn.inventory.append(idic.get_item('scroll of heal',0,0)) #defn.inventory.append(idic.get_item('scroll of minor heal',0,0)) return 'didnt-take-turn'
vbox.pack_start(label1) vbox.pack_start(hbox) vbox.pack_start(hbox2) dialog.add_button("Cancel", 0) dialog.add_button("OK", 1) dialog.show_all() return dialog verts = objects.selected_vertices() dialog = build_gui() if verts == None: gui.message(gui.MESSAGE_ERROR, "Hey you bummer! Please select some vertices!") elif dialog.run() == 1: max = 0 min = 100 for vert in verts: vert.temp = True pos = vert.position() if pos[1] < min: min = pos[1] if pos[1] > max: max = pos[1] vert.set_frame(start_frame) avg = max + min avg = avg / 2
def resolve_attack_roll(self, dice, d12_bonus, source, target): #manage defenses avoided = False def_effect = None #still need to implement unavoidable attacks if target.creature.defenses: defense_choices = [] for defense in target.creature.defenses: if defense and defense.uses > 0 and ( defense.range == self.range['type'] or defense.range == 'any'): defense_choices.append(defense) if defense_choices: defense = random.choice(defense_choices) #for now, no bonuses. Adding defense bonuses later should be easy. def_bonus = data.sum_values_from_list(source.traits, 'defense +') #daze reduction in defense roll for condition in target.creature.conditions: if condition == 'daze': def_bonus -= 2 if defense.use(def_bonus): gui.message( target.title.capitalize() + ' avoids ' + source.title + '\'s attack!', libtcod.red) avoided = True if defense.effect: def_effect = defense.effect #mage wars attack formula if not avoided: normal_damage = 0 critical_damage = 0 dice_to_roll = dice #subtract dice if weak. Later can also add aegis here. if source.creature: for condition in source.creature.conditions: if condition == 'weak' and 'magical' not in self.traits: dice_to_roll -= 1 if not ['no damage'] in self.traits: for i in range(max(dice_to_roll, 1)): roll = libtcod.random_get_int(0, 0, 2) crit = roll * libtcod.random_get_int(0, 0, 1) if ['incorporeal' ] in target.traits and not ['ethereal'] in self.traits: #unlike the game, this formula is the simplest way to implement this: normal_damage += max(roll - crit - 1, 0) critical_damage += max(crit - 1, 0) else: normal_damage += roll - crit critical_damage += crit armor = max( target.creature.armor - data.sum_values_from_list(self.traits, 'piercing +'), 0) #sturdy (vet's belt effect) for trait in target.traits: if trait[0] == 'sturdy +': if critical_damage >= trait[1]: normal_damage += trait[1] critical_damage -= trait[1] else: normal_damage += critical_damage critical_damage = 0 if ['resilient'] in target.traits: damage = critical_damage else: damage = max(normal_damage - armor, 0) + critical_damage if damage > 0: if not self.is_counterstrike: gui.message( source.title.capitalize() + ' attacks ' + target.title + ' with ' + self.name + '! ' + str(damage) + ' damage!', libtcod.red) else: gui.message( source.title.capitalize() + ' retaliates with ' + self.name + '! ' + str(damage) + ' damage!', libtcod.orange) target.creature.take_damage(damage) else: if not self.is_counterstrike: gui.message( source.title.capitalize() + ' attacks ' + target.title + ' with ' + self.name + '. No damage!', libtcod.red) else: gui.message( source.title.capitalize() + ' retaliates but fails to inflict any damage.', libtcod.orange) roll = libtcod.random_get_int(0, 1, 12) #rolling for effects effects = None if self.effects: for effect in self.effects: if roll >= effect[1]: effects = effect[0] #implement effects if effects and target.creature: #for now, don't worry about poison immunity. for effect in effects: #poison immunity if effect in ['rot', 'weak', 'cripple', 'tainted' ] and ['poison immunity'] in target.traits: gui.message(target.title + ' resists the poison!', libtcod.purple) break #long term should probably define a class of conditions or something so I don't have to maintain a list of what constitutes a condition. if effect in ['rot', 'weak', 'burn', 'cripple']: gui.message( source.title.capitalize() + ' inflicts ' + effect + ' on ' + target.title + '!', libtcod.purple) target.creature.conditions.append(effect) if effect == 'tainted': gui.message( source.title.capitalize() + 's\' attack taints ' + target.title + '!', libtcod.purple) target.creature.conditions.append('tainted') target.creature.take_damage(3) if effect == 'daze': gui.message(target.title + ' is dazed!', libtcod.purple) target.creature.conditions.append('daze') if effect == 'stun': gui.message(target.title + ' is stunned!', libtcod.purple) target.creature.conditions.append('stun') #resolve counterstrikes. currently just takes the first counterstriking attack it finds. #note that counterstrikes currently use up time #something odd going on here; sometimes target.creature has no attack attribute. #problem occurs when creature is transformed into remains. #check if target still exists as a creature, e.g. has not been killed or so forth with 'if target.creature' #long run find a better way to fix this problem if target.creature: if target.creature.attacks and self.range[ 'type'] == 'melee' and not self.is_counterstrike: for attack in target.creature.attacks: #currently returns the first counterattack it finds. special case for shield bash, rather than giving defenses effects if ([ 'counterstrike' ] in attack.traits) or (attack.name == 'shield bash' and def_effect == 'shield bash'): attack.is_counterstrike = True target.creature.attack(source, attack) attack.is_counterstrike = False break