def test(): fp = SLP_PATH + 'run_2.slp' game = slp.Game(fp) frames = game.frames[123:] i = 0 for f in frames: print(f.ports[1].leader.post.hit_stun) print("done")
def parseFile(filename): """ Return a slippi game object from a filename :param filename: filename to be read :return: slippi game object :seealso: https://py-slippi.readthedocs.io/en/latest/source/slippi.html#module-slippi.game """ game = slippi.Game(filename) return game
def parse_slp_file(slp_file, drive): try: game = slippi.Game(slp_file) except Exception as e: print("WARNING: slippi parsing exception while reading %s:" % slp_file) print("%s: %s" % (type(e), e)) print("Skipping this replay") return None #start_date = pytz.timezone(config.TIME_ZONE).localize(game.metadata.date) start_time = game.metadata.date.replace( tzinfo=pytz.timezone(config.TIME_ZONE)) end_time = start_time + datetime.timedelta(seconds=game.metadata.duration / 60.) stage = game.start.stage ports = [] numplayers = 0 for i, port in enumerate(game.frames[-1].ports): if port == None: ports.append(None) continue # TODO: more robust win/lose logic, e.g. handle timeouts and LRAstart isdead = port.leader.post.stocks == 0 charname = port.leader.post.character.name # address a weird edge case with ICs where popo dies last if charname == 'POPO': charname = 'ICE_CLIMBERS' ports.append({'char': charname, 'dead_at_end': isdead}) numplayers += 1 time_offset = datetime.timedelta(0) if drive in config.DRIVE_TIME_OFFSETS: time_offset = datetime.timedelta( seconds=config.DRIVE_TIME_OFFSETS[drive]) dct = { 'start_time': start_time - time_offset, 'end_time': end_time - time_offset, 'filename': slp_file, 'drive': drive, 'ports': ports, 'stage': stage.name, 'numplayers': numplayers, } return dct
def record_replay(replay_filepath, output_dir='../vid_out/', slowdown_factor=1.1): # paths replay_filepath = Path(replay_filepath) dolphin_folder_path = Path(r'../dolphin') executable_path = dolphin_folder_path / 'Slippi Dolphin.exe' iso_path = read_json('../settings.json')['iso_path'] tmp_output_folder = Path( '../tmp/dolphin_process_0' ) # if multiple dolphins output to same folder, there would be default filename conflicts with "framedump0.avi" real_output_folder = Path(output_dir) DEFAULT_VIDEO_NAME = 'framedump0.avi' old_video_path = tmp_output_folder / DEFAULT_VIDEO_NAME new_video_name = replay_filepath.stem + '.avi' new_video_path = real_output_folder / new_video_name # clear tmp (could have files if error on last run) shutil.rmtree(tmp_output_folder) os.mkdir(tmp_output_folder) # get how long it should take to finish playback game = slippi.Game(replay_filepath) num_frames = len(game.frames) FPS = 60 duration_sec = num_frames / FPS # prep instructions for slippi dolphin to replay comm_filepath = generate_commfile(replay_filepath) # start dolphin, giving commfile (location) and other misc flags # for standard dolphin cmd line, see # https://wiki.dolphin-emu.org/index.php?title=Help:Contents process = subprocess.Popen([ str(executable_path), '-i', str(comm_filepath), '--hide-seekbar', '-e', str(iso_path), '-b', # '-l', # '-d', '--output-directory', str(tmp_output_folder), ]) # I wish I could get a dolphin.on_start_playback and on_finish # but this crude dircheck and sleep() will have to suffice # wait until video file is created, signaling start # print('waiting on dolphin start..') while not os.listdir(tmp_output_folder): pass # print('dolphin has started playing') # wait in realtime for replay to finish # 4:02 (+123 f) of game duration only made 3:41 of video with slowdown; guess my computer is not fast enough for realtime # 244 s made 221 s, so multiply by factor of 244/221 ~= 1.104 sleep(slowdown_factor * duration_sec) # overshoot with extra time as buffer # close dolphin, which will finish recording process.terminate() # give it time to close so video file can be modified sleep(1) # abort if video is too short (ie didn't finish) if video_tools.num_frames(str(old_video_path)) < num_frames: raise Exception( 'Video did not finish recording; trying again with slowdown_factor + 0.1 ?' ) # return record_replay(replay_filepath, output_dir, slowdown_factor+0.1) return -1 # prep new location if new_video_path.exists(): os.remove(new_video_path) # move_file(old_video_path, new_video_path) # trim excess "waiting for game.." frames and move to final location video_tools.trim(0, num_frames, str(old_video_path), str(new_video_path))
def record_hs(path, plotname): game = slp.Game(path) frames = game.frames[123:] sdict = actionstate_dict() adict = attack_dict() p0_name = game.metadata.players[0].netplay_name p1_name = game.metadata.players[1].netplay_name # now try rolling average: p0_wind = [] p0_wsum = 0 p1_wind = [] p1_wsum = 0 p0_dwind = [] p0_dsum = 0 p0_hp = 0 p1_dwind = [] p1_dsum = 0 p1_hp = 0 # things to analyze p0_map = [] p0_dmap = [] p1_map = [] p1_dmap = [] p0_scores = [] p1_scores = [] fcount = [] fc = 0 for f in frames: fcount.append(fc) fc += 1 p0_ths = f.ports[0].leader.post.hit_stun p0_tdam = f.ports[0].leader.post.damage p1_ths = f.ports[1].leader.post.hit_stun p1_tdam = f.ports[1].leader.post.damage # took damage if p0_tdam > p0_hp: p0_dwind.append(p0_tdam - p0_hp) p0_dsum += p0_tdam - p0_hp else: p0_dwind.append(0) if p0_ths > 0.1: p0_wsum += p0_ths p0_wind.append(p0_ths) else: p0_wind.append(0) if p1_tdam > p1_hp: p1_dwind.append(p1_tdam - p1_hp) p1_dsum += p1_tdam - p1_hp else: p1_dwind.append(0) if p1_ths > 0.1: p1_wsum += p1_ths p1_wind.append(p1_ths) else: p1_wind.append(0) # check lengths & adjust if(len(p0_wind) > WINDOW): p0_wsum -= p0_wind[0] p0_wind.pop(0) if(len(p1_wind) > WINDOW): p1_wsum -= p1_wind[0] p1_wind.pop(0) if(len(p0_dwind) > WINDOW): p0_dsum -= p0_dwind[0] p0_dwind.pop(0) if(len(p1_dwind) > WINDOW): p1_dsum -= p1_dwind[0] p1_dwind.pop(0) p0_map.append(p0_wsum) p0_dmap.append(p0_dsum) p1_map.append(p1_wsum) p1_dmap.append(p1_dsum) # calculate combo scores # note: port 0's score is based on port 1's damage & hitstun values, as they # were a result of his attacks (same w/ port 2) p0_scores.append(comboscore(p1_dsum, p1_wsum)) p1_scores.append(comboscore(p0_dsum, p0_wsum)) # adjust hp for next frame p0_hp = p0_tdam p1_hp = p1_tdam fig, (ax1, ax2) = plt.subplots(2) ax1.plot(fcount, p0_map, label = p0_name) ax1.plot(fcount, p1_map, label = p1_name, color = 'red') ax1.set_title('hitstun map') ax1.legend() ax2.plot(fcount, p0_dmap, label = p0_name) ax2.plot(fcount, p1_dmap, label = p1_name, color = 'red') ax2.set_title('damage map') ax2.legend() fig.savefig('test_out/{}.png'.format(plotname)) scorefig, ax = plt.subplots() ax.plot(fcount, p0_scores, label = p0_name) ax.plot(fcount, p1_scores, label = p1_name) ax.legend() scorefig.savefig('test_out/{}_cscores.png'.format(plotname))
def find_combos(slp_path): game = slp.Game(slp_path) # cut the first 123 frames since its game startup frames = game.frames[123:] sdict = actionstate_dict() adict = attack_dict() # numbers to keep track of # char state: p1_prev_state = frames[0].ports[0].leader.post.state p2_prev_state = frames[0].ports[1].leader.post.state # current hitstun frames p1_hs = 0.5 p2_hs = 0.5 # each player's last percent p1_dam = 0 p2_dam = 0 # last attack landed p1_lal = None p2_lal = None # record rolling averages and stuff comboer = 0 victim = 1 window_hs = 0 state = State.NEUTRAL combo_counter = 0 # record when hits occured # key = frame, val = attack/combo start/stop hitmap = {} combomap = {} fcount = 0 for f in frames: # for now, just look @ fox since i know he's the one getting abused if f.ports[victim].leader.post.damage > p2_dam: p1_lal = f.ports[comboer].leader.post.last_attack_landed if p1_lal < 30: hitmap[fcount] = (adict[p1_lal], f.ports[victim].leader.post.damage - p2_dam) else: hitmap[fcount] = (sdict[p1_lal], f.ports[victim].leader.post.damage - p2_dam) p2_dam = f.ports[victim].leader.post.damage p2_hs = f.ports[victim].leader.post.hit_stun # rolling hitstun counter if fcount > WINDOW: window_hs -= 1 if f.ports[1].leader.post.hit_stun >= 1: window_hs += 1 if window_hs >= HST and state != State.COMBO: state = State.COMBO combomap[fcount] = "combo started" else: # check for which non-combo status # if (neutral): # if (edgeguard): # if (offstage): # if (reverse): pass fcount += 1 # write hitmap with open('ref/' + slp_path[-9:-4] + '_hitmap.json', 'w') as outfile: json.dump(hitmap, outfile, indent=4)
def record_moves(slp_path, filename): game = slp.Game(slp_path) frames = game.frames[123:] sdict = actionstate_dict() adict = attack_dict() # record the metadata of the game md = game.metadata # dict. to record new charstates # key = state, val = initial frame it was found in falco_states = {} fox_states = {} moves = {} falco_states["duration"] = md.duration fox_states["duration"] = md.duration count = 0 for f in frames: falstate = f.ports[0].leader.post.state foxstate = f.ports[1].leader.post.state last_move = f.ports[0].leader.post.last_attack_landed if falstate not in falco_states.keys(): falco_states[sdict[falstate]] = count if foxstate not in fox_states.keys(): fox_states[sdict[foxstate]] = count if last_move != None and last_move not in moves.keys(): # if moves in the common moves list: if not, pull from char_states if int(last_move) > 30: moves[sdict[last_move]] = count else: moves[adict[last_move]] = count count += 1 # reorder by value falco_states = { k: v for k, v in sorted(falco_states.items(), key=lambda item: item[1]) } fox_states = { k: v for k, v in sorted(fox_states.items(), key=lambda item: item[1]) } moves = {k: v for k, v in sorted(moves.items(), key=lambda item: item[1])} # export to a json file to look at with open('ref/' + filename + '.json', 'w') as outfile: json.dump(falco_states, outfile, indent=4) with open('ref/fox_' + filename + '.json', 'w') as outfile: json.dump(fox_states, outfile, indent=4) with open('ref/attacks_' + filename + '.json', 'w') as outfile: json.dump(moves, outfile, indent=4) # transforms the frame count to the IGT that you should be looking at def frame_to_sec(frame): # in total there are 60 * 60 * 8 frames total in a match total = 60 * 60 * 8 return total
def load(f="/Users/markvan/Slippi/Game_20200710T220209.slp"): return sl.Game(f)