def probableRangedAttack(self): """ Return an action for a ranged attack on a target if such an attack is possible. Otherwise, return None. """ if "two_square_thrower" in self.tags: if self.currentLevel.player in self.fov \ and coordinates.minimumPath(self.coords, self.currentLevel.player.coords) in range(1, 4): possible_directions = ((2,0),(2,2),(0,2),(-2,2),(-2,0),(-2,-2),(0,-2),(2,-2)) possible_targets = [coordinates.add(self.coords, i) for i in possible_directions if self.currentLevel.isEmpty(coordinates.add(self.coords, i))] visible_targets = [coords for coords in possible_targets if coords in self.fov] close_targets = [coords for coords in visible_targets if (coordinates.minimumPath(coords, self.currentLevel.player.coords) <= 1)] actual_targets = [coords for coords in close_targets if coords not in self.currentLevel.dudeLayer] if len(actual_targets) == 0: return None final_target = rng.choice(actual_targets) return action.ThrowGrenade(self, final_target) else: return None elif "twelve_square_firer" in self.tags: direction = coordinates.get_cardinal_direction(self.coords, self.currentLevel.player.coords) dist = coordinates.minimumPath(self.coords, self.currentLevel.player.coords) if self.currentLevel.player in self.fov \ and direction is not None \ and dist < 12: return action.FireArrow(self, direction, 12) else: return None else: return None
def fighting(self): """ Calculate the action of a monster who sees the player. """ if self.currentLevel.player not in self.fov: if self.player_last_location is not None: # The player has escaped! Find a likely square where he could have gone. adjacent_coords = coordinates.adjacent_coords( self.player_last_location) legal_coords = [ i for i in adjacent_coords if coordinates.legal(i, self.currentLevel.dimensions) ] passable_coords = [ i for i in legal_coords if self.currentLevel.isEmpty(i) ] out_of_vision_coords = \ [i for i in passable_coords if i not in self.fov] if len(out_of_vision_coords) > 0: # There is a possible escape route! Pursue! self.direction = coordinates.subtract( rng.choice(out_of_vision_coords), self.player_last_location) self.path = pf.find_shortest_path( self.currentLevel, self.coords, self.player_last_location, False) if self.path == []: # There is no route to the player's escape route. Wait, but stay in # state FIGHTING so as to take advantage of any route that opens up. return action.Wait(self) self.state = ais.TRAVELING return self.traveling() else: # There is no possible escape route; give up and rest. self.state = ais.RESTING return self.resting() else: assert False else: self.player_last_location = self.currentLevel.player.coords if self.AICode == "CLOSE": return self.closeToPlayer() elif self.AICode == "RANGEDAPPROACH": return self.rangedApproach() else: raise exc.InvalidDataWarning( "The monster %s has an unknown AICode, %s" % (self.name, self.AICode)) return action.Wait(self) assert False
def statue(self): """ The statue AI pattern is as follows: If a monster that meets certain conditions is in view, do a general haste. If no such monster is in view, teleport adjacent to a damaged monster. If no such monster exists, then teleport randomly. """ for d in self.fov.dudes: if self.spec == "QUICKEN": if (not d.isPlayer()) and (d.AICode != "STATUE"): return action.HasteAll(self, 8, False, False) else: assert False # If the statue is stuck, it can't teleport, so it just stays. if self.hasCondition("STUCK"): return action.Wait(self) # If the statue did not do anything to monsters in view, it must teleport. for m in self.currentLevel.dudeLayer: if m.cur_HP < m.max_HP and not m.isPlayer() and (m.AICode != "STATUE"): # Aha, a target! destination_candidates = coordinates.adjacent_coords(m.coords) destination_options = [ i for i in destination_candidates if self.currentLevel.isEmpty(i) and i not in self.currentLevel.dudeLayer ] if destination_options != []: return action.Teleport(self, rng.choice(destination_options)) # If there are no monsters to which the statue can teleport, teleport randomly. for i in range(100): destination = rng.randomPointInRect( (0, 0), (self.currentLevel.dimensions[0] - 1, self.currentLevel.dimensions[1] - 1)) if self.currentLevel.isEmpty(destination) and \ destination not in self.currentLevel.dudeLayer: return action.Teleport(self, destination) return action.Wait(self)
def getConditionAction(self): """ Get an action governed by the Dude's condition, not its decisions. Return an action if the Dude's conditions are making it take an action. Return "None" if they are not. """ possible_actions = [] for condition in self.conditions.values(): new_action = condition.getAction(self) if new_action is not None: possible_actions.append(new_action) if len(possible_actions) > 0: return rng.choice(possible_actions) else: return None
def randomDungeon(): """ Gets a random dungeon, using a very simple room/corridor model. The room/corridor model used is similar to that of Rogue; the map is divided into nine sectors, each of which is randomly called a room, a corridor, or empty. Once this is done, rooms and corridors are connected to adjacent rooms and corridors. This is coded terribly because it will be replaced someday. """ map_dimensions = (60, 60) map_nwcorner = (10, 10) sector_size = (12, 12) sector_nwcorners = {} for x in range(0, 3): for y in range(0, 3): sector_nwcorners[(x, y)] = ( map_nwcorner[0] + sector_size[0] * x, map_nwcorner[1] + sector_size[1] * y ) sector_types = {} for x in range(0, 3): for y in range(0, 3): percent = rng.randInt(1, 100) if percent <= 60: sector_types[(x, y)] = 1 # it's a room! elif percent <= 75: sector_types[(x, y)] = 2 # it's a corridor! else: sector_types[(x, y)] = 0 # it's empty! room_nwcoords = {} room_secoords = {} for sector_coords in sector_types.keys(): if sector_types[sector_coords] == 1: room_created = False sector_nw = sector_nwcorners[sector_coords] sector_se = (sector_nw[0] + sector_size[0] - 1, sector_nw[1] + sector_size[1] - 1) while not room_created: room_nw = (rng.randInt(sector_nw[0], sector_se[0]), rng.randInt(sector_nw[1], sector_se[1])) room_se = (room_nw[0] + rng.randInt(3, 8), room_nw[1] + rng.randInt(3, 8)) # check validity of room dimensions if room_se[0] <= sector_se[0] and room_se[1] <= sector_se[1]: room_nwcoords[sector_coords] = room_nw room_secoords[sector_coords] = room_se room_created = True elif sector_types[sector_coords] == 2: # A corridor is currently implemented as just a 1-space room. corridor_coords = (rng.randInt(sector_nwcorners[sector_coords][0], sector_nwcorners[sector_coords][0] + sector_size[0] - 1,), rng.randInt(sector_nwcorners[sector_coords][1], sector_nwcorners[sector_coords][1] + sector_size[1] - 1,)) room_nwcoords[sector_coords] = corridor_coords room_secoords[sector_coords] = corridor_coords # Check whether everywhere is accessible; if not, do a redo. sector_is_accessible = {} for x in range(3): for y in range(3): sector_is_accessible[(x, y)] = False for coord in ((0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)): if True not in sector_is_accessible.values() and sector_types[coord] != 0: sector_is_accessible[coord] = True if sector_is_accessible[coord] == True and sector_types[coord] != 0: for coord_adjustment in ((1, 0), (0, 1), (-1, 0), (0, -1)): adjacent_coord = coordinates.add(coord, coord_adjustment) if (adjacent_coord[0] >= 0 and adjacent_coord[0] < 3 and adjacent_coord[1] >= 0 and adjacent_coord[1] < 3): sector_is_accessible[adjacent_coord] = True for accessible in sector_is_accessible.items(): if sector_types[accessible[0]] != 0 and not accessible[1]: # Oops. Give up and try again. return randomDungeon() entrance_sector = rng.choice([coords for coords in sector_types.keys() if sector_types[coords] == 1]) exit_sector = rng.choice([coords for coords in sector_types.keys() if sector_types[coords] == 1]) entrance_coords = rng.randomPointInRect(room_nwcoords[entrance_sector], room_secoords[entrance_sector]) exit_coords = rng.randomPointInRect(room_nwcoords[exit_sector], room_secoords[exit_sector]) ret_dungeon = level.empty_dungeon(map_dimensions) for coord in ((0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)): if sector_types[coord] != 0: for x in range(room_nwcoords[coord][0], room_secoords[coord][0] + 1): for y in range(room_nwcoords[coord][1], room_secoords[coord][1] + 1): if sector_types[coord] == 1: ret_dungeon[(x, y)] = level.ROOM_INTERIOR_GLYPH else: ret_dungeon[(x, y)] = level.CORRIDOR_GLYPH for coord_adjustment in ((1, 0), (0, 1)): adjacent_coord = coordinates.add(coord, coord_adjustment) if (adjacent_coord[0] < 3 and adjacent_coord[1] < 3 and sector_types[adjacent_coord] != 0): make_corridor(ret_dungeon, rng.randomPointInRect(room_nwcoords[coord], room_secoords[coord]), rng.randomPointInRect(room_nwcoords[adjacent_coord], room_secoords[adjacent_coord])) ret_dungeon[entrance_coords] = level.UPSTAIRS_GLYPH ret_dungeon[exit_coords] = level.DOWNSTAIRS_GLYPH return ret_dungeon
def randomDungeon(): """ Gets a random dungeon, using a very simple room/corridor model. The room/corridor model used is similar to that of Rogue; the map is divided into nine sectors, each of which is randomly called a room, a corridor, or empty. Once this is done, rooms and corridors are connected to adjacent rooms and corridors. This is coded terribly because it will be replaced someday. """ map_dimensions = (MAX_SIZE_X, MAX_SIZE_Y) map_nwcorner = (NW_CORNER_X, NW_CORNER_Y) sector_size = (SECTOR_SIZE_X, SECTOR_SIZE_Y) sector_list = [(x, y) for x in range(NUM_SECTORS_X) for y in range(NUM_SECTORS_Y)] sector_nwcorners = {} for x in range(NUM_SECTORS_X): for y in range(NUM_SECTORS_Y): sector_nwcorners[(x, y)] = ( map_nwcorner[0] + sector_size[0] * x, map_nwcorner[1] + sector_size[1] * y ) sector_types = {} for x in range(NUM_SECTORS_X): for y in range(NUM_SECTORS_Y): percent = rng.randInt(1, 100) if percent <= 25: sector_types[(x, y)] = st.ROOM elif percent <= 60: sector_types[(x, y)] = st.DOUBLE_ROOM elif percent <= 75: sector_types[(x, y)] = st.CORRIDOR else: sector_types[(x, y)] = st.EMPTY room_nwcoords = {} room_secoords = {} for sector_coords in sector_types.keys(): sector_nw = sector_nwcorners[sector_coords] sector_se = (sector_nw[0] + sector_size[0] - 1, sector_nw[1] + sector_size[1] - 1) if sector_types[sector_coords] in (st.ROOM, st.DOUBLE_ROOM): (room_nwcoords[sector_coords], room_secoords[sector_coords]) \ = choose_room_corners(sector_nw, sector_se) elif sector_types[sector_coords] == st.CORRIDOR: # A corridor is currently implemented as just a 1-space room. corridor_coords = (rng.randInt(sector_nw[0], sector_se[0]), rng.randInt(sector_nw[1], sector_se[1])) room_nwcoords[sector_coords] = corridor_coords room_secoords[sector_coords] = corridor_coords # Check whether everywhere is accessible; if not, do a redo. sector_is_accessible = {} for x in range(NUM_SECTORS_X): for y in range(NUM_SECTORS_Y): sector_is_accessible[(x, y)] = False for coord in sector_list: if True not in sector_is_accessible.values() \ and sector_types[coord] != 0: sector_is_accessible[coord] = True if sector_is_accessible[coord] == True \ and sector_types[coord] != st.EMPTY: for coord_adjustment in ((1, 0), (0, 1), (-1, 0), (0, -1)): adjacent_coord = coordinates.add(coord, coord_adjustment) if (adjacent_coord[0] >= 0 and adjacent_coord[0] < NUM_SECTORS_X and adjacent_coord[1] >= 0 and adjacent_coord[1] < NUM_SECTORS_Y): sector_is_accessible[adjacent_coord] = True for accessible in sector_is_accessible.items(): if sector_types[accessible[0]] != 0 and not accessible[1]: # Oops. Give up and try again. return randomDungeon() entrance_sector = rng.choice([coords for coords in sector_types.keys() if sector_types[coords] in (st.ROOM, st.DOUBLE_ROOM)]) exit_sector = rng.choice([coords for coords in sector_types.keys() if sector_types[coords] in (st.ROOM, st.DOUBLE_ROOM)]) entrance_coords = rng.randomPointInRect(room_nwcoords[entrance_sector], room_secoords[entrance_sector]) exit_coords = rng.randomPointInRect(room_nwcoords[exit_sector], room_secoords[exit_sector]) ret_dungeon = level.empty_dungeon(map_dimensions) for coord in sector_list: if sector_types[coord] != st.EMPTY: if sector_types[coord] == st.CORRIDOR: fill_glyph = level.CORRIDOR_GLYPH else: fill_glyph = level.ROOM_INTERIOR_GLYPH arrays.fill_rect(ret_dungeon, room_nwcoords[coord], room_secoords[coord], fill_glyph) # If there is another room to the south or east, make a corridor from this room # to it. for coord_adjustment in ((1, 0), (0, 1)): adjacent_coord = coordinates.add(coord, coord_adjustment) if (adjacent_coord[0] < NUM_SECTORS_X and adjacent_coord[1] < NUM_SECTORS_Y and sector_types[adjacent_coord] != 0): make_corridor(ret_dungeon, rng.randomPointInRect(room_nwcoords[coord], room_secoords[coord]), rng.randomPointInRect(room_nwcoords[adjacent_coord], room_secoords[adjacent_coord])) # If the room type is DOUBLE_ROOM, bolt on a second room to the first. # This room can overflow! That is intentional. if sector_types[coord] == st.DOUBLE_ROOM: max_second_se = (room_secoords[coord][0] + MIN_ROOM_SIZE, room_secoords[coord][1] + MIN_ROOM_SIZE) (second_nw, second_se) = choose_room_corners( room_nwcoords[coord], max_second_se) arrays.fill_rect(ret_dungeon, second_nw, second_se, level.ROOM_INTERIOR_GLYPH) ret_dungeon[entrance_coords] = level.UPSTAIRS_GLYPH ret_dungeon[exit_coords] = level.DOWNSTAIRS_GLYPH return ret_dungeon
def get(self,atktype,location): # ---- Get location. ---------------------------- # # Read damage type. self.atktype = atktype # Determine location. if location == 'random': self.location = (hit[self.roll()],) else: self.location = (location,) # Set multiplier. if atktype in mult: self.multiplier = mult[atktype] # ---- Handle thresholds and sublocations. ---- # loc = self.location[0] rl = self.die.roll() # Low Tech Hit if loc in ['Arm', 'Leg']: r2 = self.die.roll() # Arm if 'Arm' in loc: if r2 <= 3: self.location = (loc, 'Forearm') elif r2 == 4: self.location = (loc, 'Elbow') elif r2 == 5: self.location = (loc, 'Upper Arm') elif r2 == 6: self.location = (loc, 'Shoulder') # Leg if 'Leg' in loc: if r2 <= 3: self.location = (loc, 'Shin') elif r2 == 4: self.location = (loc, 'Knee') elif r2 > 4: self.location = (loc, 'Thigh') elif loc in ['Shin', 'Knee', 'Thigh']: self.location = ('Leg', loc) loc = 'Leg' elif loc in ['Forearm', 'Elbow', 'Upper Arm', 'Shoulder']: self.location = ('Arm', loc) loc = 'Arm' if (rl == 1): # Roll of 1. # Arm or leg. if not 'Vascular' in loc and ('Arm' in loc or 'Leg' in loc ) and not ('cr' in atktype): # Vascular Hit # Get Side side = loc.split()[0] chunk = 'Arm' if ('Arm' in loc) else 'Leg' # Reformat locs if len(loc.split()) == 1 or chunk in self.location[-1]: self.location = (self.location[-1], '%s Vascular' % (chunk)) else: self.location = (side + ' ' + self.location[-1], '%s Vascular' % (chunk)) # In the face. elif 'Face' in loc: self.location = (loc, 'Skull' if (atktype in ['imp','burn'] or 'pi' in atktype) else 'Nose') # Hand or feet. elif atktype in ['cr','cut','burn'] or 'pi' in atktype and ( 'Hand' in loc or 'Foot' in loc): self.location = (choice(['Left','Right']) + ' ' + loc, 'Wrist' if ('Hand' in loc) else 'Ankle') # Neck or torso. elif atktype in ['cut','imp','burn'] or 'pi' in atktype and ( 'Neck' in loc or 'Torso' in loc): self.location = (loc, 'Neck Vascular' if ('Neck' in loc) else 'Vitals') # Abdomen if 'Abdomen' == loc: if rl == 1: self.location = (loc, 'Vitals') elif rl <= 4: self.location = (loc, 'Digestive Tract') elif rl == 5: self.location = (loc, 'Pelvis') else: self.location = (loc, 'Groin') # Face sub-location (when not already resolved). if self.facesub and 'Face' == loc and len(self.location) == 1: r1 = self.die.roll() if r1 == 1: self.location = (loc, 'Jaw') elif r1 == 2: self.location = (loc, 'Nose') elif r1 == 3: self.location = (loc, 'Ear') elif r1 == 4 or r1 == 5: self.location = (loc, 'Cheek') else: self.location = (loc, 'Eye') # Set threshold dictionary; order. self.SetThreshold() # ---- Process notes, wounds. -------------------- # notetable.NoteProcessor(self) woundtable.WoundProcessor(self) # ---- Process critical hits. -------------------- # if loc in ['Face', 'Skull', 'Eye', 'Ear', 'Nose', 'Cheek', 'Jaw']: self.CriticalHead() else: self.CriticalHit() # ---- Save data to datastore. ----------------- # self.SaveStats(atktype,location)