def start_pygame(self): """ Initialize pygame system stuff and draw empty window """ if not pygame.display.get_init(): pygame.display.init() if not pygame.font.get_init(): pygame.font.init() pygame.display.set_icon(self.get_image("collectibles_333.png")) self.clock = pygame.time.Clock() opt = Options() # figure out where we should put our window. xpos = opt.x_position ypos = opt.y_position # it can go negative when weird problems happen, so put it in a default location in that case if xpos < 0: xpos = 100 if ypos < 0: ypos = 100 os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" % (xpos, ypos) if self.screen is None and opt.transparent_mode: # If screen is none, we make our own self.screen = pygame.display.set_mode((opt.width, opt.height), NOFRAME) self.transparent_mode() elif self.screen is None: self.screen = pygame.display.set_mode((opt.width, opt.height), RESIZABLE) self.screen.fill(DrawingTool.color(opt.background_color)) self.reset_options() if platform.system() == "Windows": self.win_info = pygameWindowInfo.PygameWindowInfo() del os.environ['SDL_VIDEO_WINDOW_POS']
def run(self): os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % ( self.options["xposition"], self.options["yposition"]) # initialize pygame system stuff pygame.init() update_notifier = self.check_for_update() pygame.display.set_caption("Rebirth Item Tracker" + update_notifier) screen = pygame.display.set_mode( (self.options["width"], self.options["height"]), RESIZABLE) self.font = pygame.font.SysFont("Arial", int(8 * self.options["size_multiplier"]), bold=True) pygame.display.set_icon( self.get_image("collectibles/collectibles_333.png")) done = False clock = pygame.time.Clock() winInfo = None if platform.system() == "Windows": winInfo = pygameWindowInfo.PygameWindowInfo() del os.environ['SDL_VIDEO_WINDOW_POS'] while not done: # pygame logic for event in pygame.event.get(): if event.type == pygame.QUIT: if platform.system() == "Windows": winPos = winInfo.getScreenPosition() self.options["xposition"] = winPos["left"] self.options["yposition"] = winPos["top"] self.save_options() done = True elif event.type == VIDEORESIZE: screen = pygame.display.set_mode(event.dict['size'], RESIZABLE) self.options["width"] = event.dict["w"] self.options["height"] = event.dict["h"] self.save_options() self.reflow() pygame.display.flip() elif event.type == MOUSEMOTION: if pygame.mouse.get_focused(): x, y = pygame.mouse.get_pos() if y < len(self.item_position_index): selected_row = self.item_position_index[y] if x < len(selected_row): self.selected_item_idx = selected_row[x] if self.selected_item_idx: self.item_message_start_time = self.framecount elif event.type == KEYDOWN: if len(self.collected_items) > 0: if event.key == pygame.K_RIGHT: self.adjust_selected_item(1) elif event.key == pygame.K_LEFT: self.adjust_selected_item(-1) elif event.key == pygame.K_RETURN: self.load_selected_detail_page() elif event.type == MOUSEBUTTONDOWN: if event.button == 1: self.load_selected_detail_page() if event.button == 3: if os.path.isfile("optionpicker/option_picker.exe"): self.log_msg("Starting option picker from .exe", "D") subprocess.call(os.path.join( 'optionpicker', "option_picker.exe"), shell=True) elif os.path.isfile("option_picker.py"): self.log_msg("Starting option picker from .py", "D") subprocess.call("python option_picker.py", shell=True) else: self.log_msg("No option_picker found!", "D") self.load_options() self.selected_item_idx = None # Clear this to avoid overlapping an item that may have been hidden self.reflow() screen.fill(self.color(self.options["background_color"])) clock.tick(int(self.options["framerate_limit"])) if self.log_not_found: draw_text( screen, "log.txt not found. Put the RebirthItemTracker folder inside the isaac folder, next to log.txt", self.color(self.options["text_color"]), pygame.Rect(2, 2, self.options["width"] - 2, self.options["height"] - 2), self.font, aa=True, wrap=True) # draw item pickup text, if applicable if (len(self.collected_items) > 0 and self.options["show_description"] and self.run_start_frame + 120 < self.framecount and self.item_message_countdown_in_progress()): self.write_item_text(self.font, screen) elif (self.options["show_seed"] or self.options["show_guppy_count"]) and not self.log_not_found: # draw seed/guppy text: seed = "" guppy = "" if self.options["show_seed"]: seed = "Seed: " + self.seed if self.options["show_guppy_count"]: if len(self.collected_guppy_items) >= 3: guppy += " - Guppy: yes" else: guppy += " - Guppy: " + str( len(self.collected_guppy_items)) else: guppy = "" self.text_height = draw_text( screen, "{}{}".format(seed, guppy), self.color(self.options["text_color"]), pygame.Rect(2, 2, self.options["width"] - 2, self.options["height"] - 2), self.font, aa=True) self.reflow() else: # can only happen if you turn seed + item descriptions off in options while its running if self.text_height != 0: self.text_height = 0 self.reflow() if not self.item_message_countdown_in_progress(): self.selected_item_idx = None floor_to_draw = None # draw items on screen, excluding filtered items: for item in self.collected_item_info: if item.shown: if item.floor: floor_to_draw = item else: self.draw_item(item, screen) #don't draw a floor until we hit the next item (this way multiple floors in a row collapse) if floor_to_draw and self.options["show_floors"]: self.draw_floor(floor_to_draw, screen, self.font) # also draw the floor if we hit the end, so the current floor is visible if floor_to_draw and self.options["show_floors"]: self.draw_floor(floor_to_draw, screen, self.font) if (self.selected_item_idx and self.selected_item_idx < len(self.collected_item_info) and self.item_message_countdown_in_progress()): item = self.collected_item_info[self.selected_item_idx] if item.id not in self.floor_id_to_label: screen.blit(self.get_image(self.id_to_image(item.id)), (item.x, item.y)) pygame.draw.rect( screen, self.color(self.options["text_color"]), (item.x, item.y, int(32 * self.options["size_multiplier"]), int(32 * self.options["size_multiplier"])), 2) pygame.display.flip() self.framecount += 1 # process log stuff every read_delay seconds. making sure to truncate to an integer or else it might never mod to 0 if self.framecount % int( self.options["framerate_limit"] * self.read_delay) == 0: self.load_log_file() self.splitfile = self.content.splitlines() # return to start if seek passes the end of the file (usually b/c log file restarted) if self.seek > len(self.splitfile): self.log_msg( "Current line number longer than lines in file, returning to start of file", "D") self.seek = 0 should_reflow = False # process log's new output for current_line_number, line in enumerate( self.splitfile[self.seek:]): self.log_msg(line, "V") # end floor boss defeated, hopefully? if line.startswith('Mom clear time:'): kill_time = int(line.split(" ")[-1]) # if you re-enter a room you get a "mom clear time" again, check for that. # can you fight the same boss twice? if self.current_room not in [ x[0] for x in self.bosses ]: self.bosses.append((self.current_room, kill_time)) self.log_msg( "Defeated %s%s boss %s at time %s" % (len(self.bosses), self.suffix(len( self.bosses)), self.current_room, kill_time), "D") # check + handle the end of the run (order important here!) # we want it after boss kill (so we have that handled) but before RNG Start Seed (so we can handle that) self.check_end_run(line, current_line_number + self.seek) # start of a run if line.startswith('RNG Start Seed:'): # this assumes a fixed width, but from what i see it seems safe self.seed = line[16:25] self.log_msg("Starting new run, seed: %s" % self.seed, "D") self.run_start_frame = self.framecount self.rolled_item_indices = [] self.collected_items = [] self.collected_guppy_items = [] self.collected_blind_item_indices = [] self.log_msg("Emptied item array", "D") self.bosses = [] self.log_msg("Emptied boss array", "D") self.run_start_line = current_line_number + self.seek self.run_ended = False with open("seed.txt", "w") as f: f.write(self.seed) # entered a room, use to keep track of bosses if line.startswith('Room'): self.current_room = re.search('\((.*)\)', line).group(1) if 'Start Room' not in line: self.getting_start_items = False self.log_msg("Entered room: %s" % self.current_room, "D") if line.startswith('Level::Init'): self.current_floor = tuple([ re.search( "Level::Init m_Stage (\d+), m_AltStage (\d+)", line).group(x) for x in [1, 2] ]) self.blind_floor = False # assume floors aren't blind until we see they are self.getting_start_items = True floor = int(self.current_floor[0]) alt = self.current_floor[1] # special handling for cath and chest if alt == '1' and (floor == 9 or floor == 11): floor += 1 self.collected_items.append('f' + str(floor)) should_reflow = True if line.startswith('Curse of the Labyrinth!'): #it SHOULD always begin with f (that is, it's a floor) because this line only comes right after the floor line if self.collected_items[-1].startswith('f'): self.collected_items[-1] += 'x' if line.startswith('Curse of Blind'): self.blind_floor = True if line.startswith('Spawn co-player!'): self.spawned_coop_baby = current_line_number + self.seek if re.search("Added \d+ Collectibles", line): self.log_msg("Reroll detected!", "D") self.rolled_item_indices = [ index for index, item in enumerate(self.collected_items) if item[0] != 'f' ] if line.startswith('Adding collectible'): if len(self.splitfile) > 1 and self.splitfile[ current_line_number + self.seek - 1] == line: self.log_msg( "Skipped duplicate item line from baby presence", "D") continue # hacky string manip, idgaf space_split = line.split(" ") # string has the form "Adding collectible 105 (The D6)" item_id = space_split[2] if ((current_line_number + self.seek) - self.spawned_coop_baby) < ( len(self.collected_items) + 10) and item_id in self.collected_items: self.log_msg( "Skipped duplicate item line from baby entry", "D") continue item_name = " ".join(space_split[3:])[1:-1] self.log_msg( "Picked up item. id: %s, name: %s" % (item_id, item_name), "D") id_padded = item_id.zfill(3) item_info = self.items_info[id_padded] with open("itemInfo.txt", "w") as f: desc = self.generateItemDescription(item_info) f.write(item_info["name"] + ":" + desc) # ignore repeated pickups of space bar items if not (item_info.get("space") and item_id in self.collected_items): self.collected_items.append(item_id) self.item_message_start_time = self.framecount self.item_pickup_time = self.framecount else: self.log_msg( "Skipped adding item %s to avoid space-bar duplicate" % item_id, "D") if "guppy" in item_info and item_info.get( "guppy" ) and item_id not in self.collected_guppy_items: self.collected_guppy_items.append(item_id) if self.blind_floor and not self.getting_start_items: # the item we just picked up was picked up blind, so add its index here to track that fact self.collected_blind_item_indices.append( len(self.collected_items) - 1) should_reflow = True self.seek = len(self.splitfile) if should_reflow: self.reflow()
def run(self): os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" % (self.options[Option.X_POSITION], self.options[Option.Y_POSITION]) # initialize pygame system stuff pygame.init() update_notifier = self.check_for_update() pygame.display.set_caption("Rebirth Item Tracker v0.8" + update_notifier) screen = pygame.display.set_mode((self.options[Option.WIDTH], self.options[Option.HEIGHT]), RESIZABLE) self.font = pygame.font.SysFont(self.options[Option.SHOW_FONT], int(8 * self.options[Option.SIZE_MULTIPLIER]), bold=self.options[Option.BOLD_FONT]) pygame.display.set_icon(self.get_image("collectibles/collectibles_333.png")) done = False clock = pygame.time.Clock() winInfo = None if platform.system() == "Windows": winInfo = pygameWindowInfo.PygameWindowInfo() del os.environ['SDL_VIDEO_WINDOW_POS'] while not done: # pygame logic for event in pygame.event.get(): if event.type == pygame.QUIT: if platform.system() == "Windows": winPos = winInfo.getScreenPosition() self.options[Option.X_POSITION] = winPos["left"] self.options[Option.Y_POSITION] = winPos["top"] self.save_options() done = True elif event.type == VIDEORESIZE: screen = pygame.display.set_mode(event.dict['size'], RESIZABLE) self.options[Option.WIDTH] = event.dict["w"] self.options[Option.HEIGHT] = event.dict["h"] self.save_options() self.reflow() pygame.display.flip() elif event.type == MOUSEMOTION: if pygame.mouse.get_focused(): x, y = pygame.mouse.get_pos() if y < len(self.item_position_index): selected_row = self.item_position_index[y] if x < len(selected_row): self.selected_item_idx = selected_row[x] if self.selected_item_idx: self.item_message_start_time = self.framecount elif event.type == KEYDOWN: if len(self.collected_items) > 0: if event.key == pygame.K_RIGHT: self.adjust_selected_item(1) elif event.key == pygame.K_LEFT: self.adjust_selected_item(-1) elif event.key == pygame.K_RETURN: self.load_selected_detail_page() elif event.key == pygame.K_c and pygame.key.get_mods() & pygame.KMOD_CTRL: self.generate_run_summary() elif event.type == MOUSEBUTTONDOWN: if event.button == 1: self.load_selected_detail_page() if event.button == 3: import option_picker pygame.event.set_blocked([QUIT, MOUSEBUTTONDOWN, KEYDOWN, MOUSEMOTION]) option_picker.options_menu().run() pygame.event.set_allowed([QUIT, MOUSEBUTTONDOWN, KEYDOWN, MOUSEMOTION]) self.load_options() self.selected_item_idx = None # Clear this to avoid overlapping an item that may have been hidden self.reflow() screen.fill(self.color(self.options[Option.BACKGROUND_COLOR])) clock.tick(int(self.options[Option.FRAMERATE_LIMIT])) if self.log_not_found: draw_text( screen, "log.txt not found. Put the RebirthItemTracker folder inside the isaac folder, next to log.txt", self.color(self.options[Option.TEXT_COLOR]), pygame.Rect(2, 2, self.options[Option.WIDTH] - 2, self.options[Option.HEIGHT] - 2), self.font, aa=True, wrap=True ) # 19 pixels is the default line height, but we don't know what the line height is with respect to the user's particular size_multiplier. # Thus, we can just draw a single space to ensure that the spacing is consistent whether text happens to be showing or not. if self.options[Option.SHOW_DESCRIPTION] or self.options[Option.SHOW_CUSTOM_MESSAGE]: self.text_height = draw_text( screen, " ", self.color(self.options[Option.TEXT_COLOR]), pygame.Rect(2, 2, self.options[Option.WIDTH] - 2, self.options[Option.HEIGHT] - 2), self.font, aa=True, wrap=self.options[Option.WORD_WRAP] ) else: self.text_height = 0 text_written = False # draw item pickup text, if applicable if (len(self.collected_items) > 0 and self.options[Option.SHOW_DESCRIPTION] and self.run_start_frame + 120 < self.framecount and self.item_message_countdown_in_progress()): text_written = self.write_item_text(self.font, screen) if not text_written and self.options[ Option.SHOW_CUSTOM_MESSAGE] and not self.log_not_found: # draw seed/guppy text: seed = self.seed dic = defaultdict(str, seed=seed) dic.update(self.player_stats_display) # Use vformat to handle the case where the user adds an undefined # placeholder in default_message message = string.Formatter().vformat( self.options[Option.CUSTOM_MESSAGE], (), dic ) self.text_height = draw_text( screen, message, self.color(self.options[Option.TEXT_COLOR]), pygame.Rect(2, 2, self.options[Option.WIDTH] - 2, self.options[Option.HEIGHT] - 2), self.font, aa=True, wrap=self.options[Option.WORD_WRAP] ) self.reflow() if not self.item_message_countdown_in_progress(): self.selected_item_idx = None floor_to_draw = None # draw items on screen, excluding filtered items: for item in self.collected_item_info: if item.shown: if item.floor: floor_to_draw = item else: self.draw_item(item, screen) # don't draw a floor until we hit the next item (this way multiple floors in a row collapse) if floor_to_draw and self.options[Option.SHOW_FLOORS]: self.draw_floor(floor_to_draw, screen, self.font) # also draw the floor if we hit the end, so the current floor is visible if floor_to_draw and self.options[Option.SHOW_FLOORS]: self.draw_floor(floor_to_draw, screen, self.font) if (self.selected_item_idx and self.selected_item_idx < len(self.collected_item_info) and self.item_message_countdown_in_progress()): item = self.collected_item_info[self.selected_item_idx] if item.id not in self.floor_id_to_label: screen.blit(self.get_image(self.id_to_image(item.id)), (item.x, item.y)) size_multiplier = int(32 * self.options[Option.SIZE_MULTIPLIER]) pygame.draw.rect( screen, self.color(self.options[Option.TEXT_COLOR]), (item.x, item.y, size_multiplier, size_multiplier), 2 ) pygame.display.flip() self.framecount += 1 # process log stuff every read_delay seconds. making sure to truncate to an integer or else it might never mod to 0 if self.framecount % int(self.options[Option.FRAMERATE_LIMIT] * self.read_delay) == 0: self.load_log_file() self.splitfile = self.content.splitlines() # return to start if seek passes the end of the file (usually b/c log file restarted) if self.seek > len(self.splitfile): self.log_msg("Current line number longer than lines in file, returning to start of file", "D") self.seek = 0 should_reflow = False # process log's new output for current_line_number, line in enumerate(self.splitfile[self.seek:]): self.log_msg(line, "V") # end floor boss defeated, hopefully? if line.startswith('Mom clear time:'): kill_time = int(line.split(" ")[-1]) # if you re-enter a room you get a "mom clear time" again, check for that. # can you fight the same boss twice? if self.current_room not in [x[0] for x in self.bosses]: self.bosses.append((self.current_room, kill_time)) self.log_msg( "Defeated %s%s boss %s at time %s" % (len(self.bosses), self.suffix(len(self.bosses)), self.current_room, kill_time), "D") # check + handle the end of the run (order important here!) # we want it after boss kill (so we have that handled) but before RNG Start Seed (so we can handle that) self.check_end_run(line, current_line_number + self.seek) # start of a run if line.startswith('RNG Start Seed:'): # this assumes a fixed width, but from what I see it seems safe self.seed = line[16:25] self.log_msg("Starting new run, seed: %s" % self.seed, "D") self.run_start_frame = self.framecount self.rolled_item_indices = [] self.collected_items = [] self.collected_guppy_items = [] self.collected_blind_item_indices = [] self.log_msg("Emptied item array", "D") self.bosses = [] self.log_msg("Emptied boss array", "D") self.run_start_line = current_line_number + self.seek self.run_ended = False self.reset_player_stats() with open("overlay text/seed.txt", "w+") as f: f.write(self.seed) # entered a room, use to keep track of bosses if line.startswith('Room'): self.current_room = re.search('\((.*)\)', line).group(1) if 'Start Room' not in line: self.getting_start_items = False self.log_msg("Entered room: %s" % self.current_room, "D") if line.startswith('Level::Init'): self.current_floor = tuple( [re.search("Level::Init m_Stage (\d+), m_AltStage (\d+)", line).group(x) for x in [1, 2]]) # assume floors aren't cursed until we see they are self.blind_floor = False self.getting_start_items = True floor = int(self.current_floor[0]) alt = self.current_floor[1] # special handling for cath and chest if alt == '1' and (floor == 9 or floor == 11): floor += 1 floor_id = 'f' + str(floor) self.collected_items.append(floor_id) # TODO: remove this line - items are not floors self.floors.append(Floor(floor_id)) should_reflow = True last_collected = self.collected_items[-1] if self.collected_items else None if line.startswith("Curse of the Labyrinth!"): # it SHOULD always begin with f (that is, it's a floor) because this line only comes right after the floor line if last_collected.startswith('f'): self.collected_items[-1] += 'x' if line.startswith("Curse of Blind"): self.floors[-1].blind = True self.blind_floor = True if line.startswith("Curse of the Lost!"): self.floors[-1].lost = True if line.startswith("Spawn co-player!"): self.spawned_coop_baby = current_line_number + self.seek if re.search("Added \d+ Collectibles", line): self.log_msg("Reroll detected!", "D") self.rolled_item_indices = [index for index, item in enumerate(self.collected_items) if item[0] != 'f'] if line.startswith('Adding collectible'): if len(self.splitfile) > 1 and self.splitfile[current_line_number + self.seek - 1] == line: self.log_msg("Skipped duplicate item line from baby presence", "D") continue # hacky string manip, idgaf space_split = line.split(" ") # string has the form "Adding collectible 105 (The D6)" item_id = space_split[2] if ((current_line_number + self.seek) - self.spawned_coop_baby) < (len(self.collected_items) + 10)\ and item_id in self.collected_items: self.log_msg("Skipped duplicate item line from baby entry", "D") continue item_name = " ".join(space_split[3:])[1:-1] self.log_msg("Picked up item. id: %s, name: %s" % (item_id, item_name), "D") item_info = self.get_item_info(item_id) with open("overlay text/itemInfo.txt", "w+") as f: desc = self.generate_item_description(item_info) f.write(item_info[ItemProperty.NAME] + ":" + desc) # ignore repeated pickups of space bar items if not (item_info.get(ItemProperty.SPACE) and item_id in self.collected_items): self.collected_items.append(item_id) self.item_message_start_time = self.framecount self.item_pickup_time = self.framecount else: self.log_msg("Skipped adding item %s to avoid space-bar duplicate" % item_id, "D") self.add_stats_for_item(item_info, item_id) if self.blind_floor and not self.getting_start_items: # the item we just picked up was picked up blind, so add its index here to track that fact self.collected_blind_item_indices.append(len(self.collected_items) - 1) should_reflow = True self.seek = len(self.splitfile) if should_reflow: self.reflow()