def __handle_slippstream_events(self, event_bytes, gamestate): """ Handle a series of events, provided sequentially in a byte array """ gamestate.menu_state = enums.Menu.IN_GAME while len(event_bytes) > 0: event_size = self.eventsize[event_bytes[0]] if len(event_bytes) < event_size: print("WARNING: Something went wrong unpacking events. Data is probably missing") print("\tDidn't have enough data for event") return False if EventType(event_bytes[0]) == EventType.PAYLOADS: cursor = 0x2 payload_size = event_bytes[1] num_commands = (payload_size - 1) // 3 for i in range(0, num_commands): command, command_len = unpack(">bH", event_bytes[cursor:cursor+3]) self.eventsize[command] = command_len+1 cursor += 3 event_bytes = event_bytes[payload_size + 1:] elif EventType(event_bytes[0]) == EventType.FRAME_START: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.GAME_START: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.GAME_END: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.PRE_FRAME: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.POST_FRAME: gamestate.frame = unpack(">i", event_bytes[0x1:0x1+4])[0] controller_port = unpack(">B", event_bytes[0x5:0x5+1])[0] + 1 gamestate.player[controller_port].x = unpack(">f", event_bytes[0xa:0xa+4])[0] gamestate.player[controller_port].y = unpack(">f", event_bytes[0xe:0xe+4])[0] gamestate.player[controller_port].character = enums.Character(unpack(">B", event_bytes[0x7:0x7+1])[0]) try: gamestate.player[controller_port].action = enums.Action(unpack(">H", event_bytes[0x8:0x8+2])[0]) except ValueError: gamestate.player[controller_port].action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert facing_float = unpack(">f", event_bytes[0x12:0x12+4])[0] gamestate.player[controller_port].facing = facing_float > 0 gamestate.player[controller_port].percent = int(unpack(">f", event_bytes[0x16:0x16+4])[0]) gamestate.player[controller_port].stock = unpack(">B", event_bytes[0x21:0x21+1])[0] gamestate.player[controller_port].action_frame = int(unpack(">f", event_bytes[0x22:0x22+4])[0]) # Extract the bit at mask 0x20 bitflags2 = unpack(">B", event_bytes[0x27:0x27+1])[0] gamestate.player[controller_port].hitlag = bool(bitflags2 & 0x20) try: gamestate.player[controller_port].hitstun_frames_left = int(unpack(">f", event_bytes[0x2b:0x2b+4])[0]) except ValueError: gamestate.player[controller_port].hitstun_frames_left = 0 gamestate.player[controller_port].on_ground = not bool(unpack(">B", event_bytes[0x2f:0x2f+1])[0]) gamestate.player[controller_port].jumps_left = unpack(">B", event_bytes[0x32:0x32+1])[0] gamestate.player[controller_port].invulnerable = int(unpack(">B", event_bytes[0x34:0x34+1])[0]) != 0 try: gamestate.player[controller_port].speed_air_x_self = unpack(">f", event_bytes[0x35:0x35+4])[0] except error: gamestate.player[controller_port].speed_air_x_self = 0 try: gamestate.player[controller_port].speed_y_self = unpack(">f", event_bytes[0x39:0x39+4])[0] except error: gamestate.player[controller_port].speed_y_self = 0 try: gamestate.player[controller_port].speed_x_attack = unpack(">f", event_bytes[0x3d:0x3d+4])[0] except error: gamestate.player[controller_port].speed_x_attack = 0 try: gamestate.player[controller_port].speed_y_attack = unpack(">f", event_bytes[0x41:0x41+4])[0] except error: gamestate.player[controller_port].speed_y_attack = 0 try: gamestate.player[controller_port].speed_ground_x_self = unpack(">f", event_bytes[0x45:0x45+4])[0] except error: gamestate.player[controller_port].speed_ground_x_self = 0 # Keep track of a player's invulnerability due to respawn or ledge grab gamestate.player[controller_port].invulnerability_left = max(0, self._prev_gamestate.player[controller_port].invulnerability_left - 1) if gamestate.player[controller_port].action == Action.ON_HALO_WAIT: gamestate.player[controller_port].invulnerability_left = 120 # Don't give invulnerability to the first descent if gamestate.player[controller_port].action == Action.ON_HALO_DESCENT and gamestate.frame > 150: gamestate.player[controller_port].invulnerability_left = 120 if gamestate.player[controller_port].action == Action.EDGE_CATCHING and gamestate.player[controller_port].action_frame == 1: gamestate.player[controller_port].invulnerability_left = 36 # The pre-warning occurs when we first start a dash dance. if gamestate.player[controller_port].action == Action.DASHING and \ self._prev_gamestate.player[controller_port].action not in [Action.DASHING, Action.TURNING]: gamestate.player[controller_port].moonwalkwarning = True # Take off the warning if the player does an action other than dashing if gamestate.player[controller_port].action != Action.DASHING: gamestate.player[controller_port].moonwalkwarning = False # "off_stage" helper if (abs(gamestate.player[controller_port].x) > stages.EDGE_GROUND_POSITION[gamestate.stage] or \ gamestate.player[controller_port].y < -6) and not gamestate.player[controller_port].on_ground: gamestate.player[controller_port].off_stage = True else: gamestate.player[controller_port].off_stage = False event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.GECKO_CODES: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.FRAME_BOOKEND: self._prev_gamestate = gamestate # Calculate helper distance variable # This is a bit kludgey.... :/ i = 0 player_one_x, player_one_y, player_two_x, player_two_y = 0, 0, 0, 0 for _, player_state in gamestate.player.items(): if i == 0: player_one_x, player_one_y = player_state.x, player_state.y if i == 1: player_two_x, player_two_y = player_state.x, player_state.y i += 1 xdist = player_one_x - player_two_x ydist = player_one_y - player_two_y gamestate.distance = math.sqrt((xdist**2) + (ydist**2)) event_bytes = event_bytes[event_size:] return True elif EventType(event_bytes[0]) == EventType.ITEM_UPDATE: projectile = Projectile() projectile.x = unpack(">f", event_bytes[0x14:0x14+4])[0] projectile.y = unpack(">f", event_bytes[0x18:0x18+4])[0] projectile.x_speed = unpack(">f", event_bytes[0x0c:0x0c+4])[0] projectile.y_speed = unpack(">f", event_bytes[0x10:0x10+4])[0] try: projectile.owner = unpack(">B", event_bytes[0x2A:0x2A+1])[0] + 1 if projectile.owner > 4: projectile.owner = -1 except error: projectile.owner = -1 try: projectile.subtype = enums.ProjectileSubtype(unpack(">H", event_bytes[0x05:0x05+2])[0]) except ValueError: projectile.subtype = enums.ProjectileSubtype.UNKNOWN_PROJECTILE # Add the projectile to the gamestate list gamestate.projectiles.append(projectile) event_bytes = event_bytes[event_size:] else: print("WARNING: Something went wrong unpacking events. " + \ "Data is probably missing") print("\tGot invalid event type: ", event_bytes[0]) return False return False
def __post_frame(self, gamestate, event_bytes): gamestate.stage = self._current_stage gamestate.frame = np.ndarray((1,), ">i", event_bytes, 0x1)[0] controller_port = np.ndarray((1,), ">B", event_bytes, 0x5)[0] + 1 if controller_port not in gamestate.players: gamestate.players[controller_port] = PlayerState() playerstate = gamestate.players[controller_port] # Is this Nana? if np.ndarray((1,), ">B", event_bytes, 0x6)[0] == 1: playerstate.nana = PlayerState() playerstate = playerstate.nana playerstate.position.x = np.ndarray((1,), ">f", event_bytes, 0xa)[0] playerstate.position.y = np.ndarray((1,), ">f", event_bytes, 0xe)[0] playerstate.x = playerstate.position.x playerstate.y = playerstate.position.y playerstate.character = enums.Character(np.ndarray((1,), ">B", event_bytes, 0x7)[0]) try: playerstate.action = enums.Action(np.ndarray((1,), ">H", event_bytes, 0x8)[0]) except ValueError: playerstate.action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert playerstate.facing = np.ndarray((1,), ">f", event_bytes, 0x12)[0] > 0 playerstate.percent = int(np.ndarray((1,), ">f", event_bytes, 0x16)[0]) playerstate.shield_strength = np.ndarray((1,), ">f", event_bytes, 0x1A)[0] playerstate.stock = np.ndarray((1,), ">B", event_bytes, 0x21)[0] playerstate.action_frame = int(np.ndarray((1,), ">f", event_bytes, 0x22)[0]) try: playerstate.hitstun_frames_left = int(np.ndarray((1,), ">f", event_bytes, 0x2B)[0]) except TypeError: playerstate.hitstun_frames_left = 0 except ValueError: playerstate.hitstun_frames_left = 0 try: playerstate.on_ground = not bool(np.ndarray((1,), ">B", event_bytes, 0x2F)[0]) except TypeError: playerstate.on_ground = True try: playerstate.jumps_left = np.ndarray((1,), ">B", event_bytes, 0x32)[0] except TypeError: playerstate.jumps_left = 1 try: playerstate.invulnerable = int(np.ndarray((1,), ">B", event_bytes, 0x34)[0]) != 0 except TypeError: playerstate.invulnerable = False try: playerstate.speed_air_x_self = np.ndarray((1,), ">f", event_bytes, 0x35)[0] except TypeError: playerstate.speed_air_x_self = 0 try: playerstate.speed_y_self = np.ndarray((1,), ">f", event_bytes, 0x39)[0] except TypeError: playerstate.speed_y_self = 0 try: playerstate.speed_x_attack = np.ndarray((1,), ">f", event_bytes, 0x3D)[0] except TypeError: playerstate.speed_x_attack = 0 try: playerstate.speed_y_attack = np.ndarray((1,), ">f", event_bytes, 0x41)[0] except TypeError: playerstate.speed_y_attack = 0 try: playerstate.speed_ground_x_self = np.ndarray((1,), ">f", event_bytes, 0x45)[0] except TypeError: playerstate.speed_ground_x_self = 0 try: playerstate.hitlag_left = int(np.ndarray((1,), ">f", event_bytes, 0x49)[0]) except TypeError: playerstate.hitlag_left = 0 # Keep track of a player's invulnerability due to respawn or ledge grab if controller_port in self._prev_gamestate.players: playerstate.invulnerability_left = max(0, self._invuln_start[controller_port][1] - (gamestate.frame - self._invuln_start[controller_port][0])) if playerstate.action == Action.ON_HALO_WAIT: playerstate.invulnerability_left = 120 self._invuln_start[controller_port] = (gamestate.frame, 120) # Don't give invulnerability to the first descent if playerstate.action == Action.ON_HALO_DESCENT and gamestate.frame > 150: playerstate.invulnerability_left = 120 self._invuln_start[controller_port] = (gamestate.frame, 120) if playerstate.action == Action.EDGE_CATCHING and playerstate.action_frame == 1: playerstate.invulnerability_left = 36 self._invuln_start[controller_port] = (gamestate.frame, 36) # First frame of the game if gamestate.frame == -123: playerstate.invulnerability_left = 0 self._invuln_start[controller_port] = (gamestate.frame, 0) # The pre-warning occurs when we first start a dash dance. if controller_port in self._prev_gamestate.players: if playerstate.action == Action.DASHING and \ self._prev_gamestate.players[controller_port].action not in [Action.DASHING, Action.TURNING]: playerstate.moonwalkwarning = True # Take off the warning if the player does an action other than dashing if playerstate.action != Action.DASHING: playerstate.moonwalkwarning = False # "off_stage" helper try: if (abs(playerstate.position.x) > stages.EDGE_GROUND_POSITION[gamestate.stage] or \ playerstate.y < -6) and not playerstate.on_ground: playerstate.off_stage = True else: playerstate.off_stage = False except KeyError: playerstate.off_stage = False # ECB top edge, x ecb_top_x = 0 ecb_top_y = 0 try: ecb_top_x = np.ndarray((1,), ">f", event_bytes, 0x4D)[0] except TypeError: ecb_top_x = 0 # ECB Top edge, y try: ecb_top_y = np.ndarray((1,), ">f", event_bytes, 0x51)[0] except TypeError: ecb_top_y = 0 playerstate.ecb.top = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.top.x = ecb_top_x playerstate.ecb.top.y = ecb_top_y playerstate.ecb_top = (ecb_top_x, ecb_top_y) # ECB bottom edge, x coord ecb_bot_x = 0 ecb_bot_y = 0 try: ecb_bot_x = np.ndarray((1,), ">f", event_bytes, 0x55)[0] except TypeError: ecb_bot_x = 0 # ECB Bottom edge, y coord try: ecb_bot_y = np.ndarray((1,), ">f", event_bytes, 0x59)[0] except TypeError: ecb_bot_y = 0 playerstate.ecb.bottom = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.bottom.x = ecb_bot_x playerstate.ecb.bottom.y = ecb_bot_y playerstate.ecb_bottom = (ecb_bot_x, ecb_bot_y) # ECB left edge, x coord ecb_left_x = 0 ecb_left_y = 0 try: ecb_left_x = np.ndarray((1,), ">f", event_bytes, 0x5D)[0] except TypeError: ecb_left_x = 0 # ECB left edge, y coord try: ecb_left_y = np.ndarray((1,), ">f", event_bytes, 0x61)[0] except TypeError: ecb_left_y = 0 playerstate.ecb.left = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.left.x = ecb_left_x playerstate.ecb.left.y = ecb_left_y playerstate.ecb_left = (ecb_left_x, ecb_left_y) # ECB right edge, x coord ecb_right_x = 0 ecb_right_y = 0 try: ecb_right_x = np.ndarray((1,), ">f", event_bytes, 0x65)[0] except TypeError: ecb_right_x = 0 # ECB right edge, y coord try: ecb_right_y = np.ndarray((1,), ">f", event_bytes, 0x69)[0] except TypeError: ecb_right_y = 0 playerstate.ecb.right = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.right.x = ecb_right_x playerstate.ecb.right.y = ecb_right_y playerstate.ecb_right = (ecb_right_x, ecb_right_y) if self._use_manual_bookends: self._frame = gamestate.frame
def __handle_slippstream_events(self, event_bytes, gamestate): """ Handle a series of events, provided sequentially in a byte array """ lastmessage = EventType.GAME_START while len(event_bytes) > 0: lastmessage = EventType(event_bytes[0]) event_size = self.eventsize[event_bytes[0]] if len(event_bytes) < event_size: print( "WARNING: Something went wrong unpacking events. Data is probably missing" ) print("\tDidn't have enough data for event") return False if (EventType(event_bytes[0]) == EventType.PAYLOADS): cursor = 0x2 payload_size = event_bytes[1] num_commands = (payload_size - 1) // 3 for i in range(0, num_commands): command, command_len = unpack( ">bH", event_bytes[cursor:cursor + 3]) self.eventsize[command] = command_len + 1 cursor += 3 event_bytes = event_bytes[payload_size + 1:] continue elif (EventType(event_bytes[0]) == EventType.FRAME_START): self.frame_num = unpack(">i", event_bytes[1:5])[0] event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.GAME_START): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.GAME_END): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.PRE_FRAME): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.POST_FRAME): gamestate.frame = unpack(">i", event_bytes[0x1:0x1 + 4])[0] controller_port = unpack(">B", event_bytes[0x5:0x5 + 1])[0] + 1 gamestate.player[controller_port].x = unpack( ">f", event_bytes[0xa:0xa + 4])[0] gamestate.player[controller_port].y = unpack( ">f", event_bytes[0xe:0xe + 4])[0] gamestate.player[controller_port].character = enums.Character( unpack(">B", event_bytes[0x7:0x7 + 1])[0]) try: gamestate.player[controller_port].action = enums.Action( unpack(">H", event_bytes[0x8:0x8 + 2])[0]) except ValueError: gamestate.player[ controller_port].action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert facing_float = unpack(">f", event_bytes[0x12:0x12 + 4])[0] gamestate.player[controller_port].facing = facing_float > 0 gamestate.player[controller_port].percent = int( unpack(">f", event_bytes[0x16:0x16 + 4])[0]) gamestate.player[controller_port].stock = unpack( ">B", event_bytes[0x21:0x21 + 1])[0] gamestate.player[controller_port].action_frame = int( unpack(">f", event_bytes[0x22:0x22 + 4])[0]) # Extract the bit at mask 0x20 bitflags2 = unpack(">B", event_bytes[0x27:0x27 + 1])[0] gamestate.player[controller_port].hitlag = bool(bitflags2 & 0x20) gamestate.player[controller_port].hitstun_frames_left = int( unpack(">f", event_bytes[0x2b:0x2b + 4])[0]) gamestate.player[controller_port].on_ground = not bool( unpack(">B", event_bytes[0x2f:0x2f + 1])[0]) gamestate.player[controller_port].jumps_left = unpack( ">B", event_bytes[0x32:0x32 + 1])[0] event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.GECKO_CODES): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.FRAME_BOOKEND): event_bytes = event_bytes[event_size:] return True elif (EventType(event_bytes[0]) == EventType.ITEM_UPDATE): # TODO projectiles projectile = Projectile() projectile.x = unpack(">f", event_bytes[0x14:0x14 + 4])[0] projectile.y = unpack(">f", event_bytes[0x18:0x18 + 4])[0] projectile.x_speed = unpack(">f", event_bytes[0x0c:0x0c + 4])[0] projectile.y_speed = unpack(">f", event_bytes[0x10:0x10 + 4])[0] try: projectile.subtype = enums.ProjectileSubtype( unpack(">H", event_bytes[0x05:0x05 + 2])[0]) except ValueError: projectile.subtype = enums.UNKNOWN_PROJECTILE # Add the projectile to the gamestate list gamestate.projectiles.append(projectile) event_bytes = event_bytes[event_size:] continue else: print("WARNING: Something went wrong unpacking events. " + \ "Data is probably missing") print("\tGot invalid event type: ", event_bytes[0]) return False return False
def update(self, mem_update): label = self.locations[mem_update[0]][0] player_int = int(self.locations[mem_update[0]][1]) if label == "frame": self.frame = unpack('<I', mem_update[1])[0] self.newframe = True #Now that the frame is ready, let's calculate some derived information # These are not stored inside Melee anywhere, but are nonetheless # important pieces of information that we don't want to make the # user have to re-calculate on their own for i in self.player: # Move current x,y over to prev self.player[i].prev_x = self.player[i].x self.player[i].prev_y = self.player[i].y # Move future x,y over to current self.player[i].x = self.player[i].next_x self.player[i].y = self.player[i].next_y if (abs(self.player[i].x) > stages.edgegroundposition(self.stage) or \ self.player[i].y < -6) and not self.player[i].on_ground: self.player[i].off_stage = True else: self.player[i].off_stage = False # Keep track of a player's invulnerability due to respawn or ledge grab self.player[i].invulnerability_left = max( 0, self.player[i].invulnerability_left - 1) if self.player[i].action == Action.ON_HALO_WAIT: self.player[i].invulnerability_left = 120 # Don't give invulnerability to the first descent if self.player[ i].action == Action.ON_HALO_DESCENT and self.frame > 150: self.player[i].invulnerability_left = 120 if self.player[ i].action == Action.EDGE_CATCHING and self.player[ i].action_frame == 1: self.player[i].invulnerability_left = 36 # Which character are we right now? if self.player[i].character in [ Character.SHEIK, Character.ZELDA ]: if self.player[i].transformed == self.player[i].iszelda: self.player[i].character = Character.SHEIK else: self.player[i].character = Character.ZELDA # If the player is transformed, then copy over the sub-character attributes if self.player[i].transformed: self.player[i].action = self.player[i + 4].action self.player[i].action_counter = self.player[ i + 4].action_counter self.player[i].action_frame = self.player[i + 4].action_frame self.player[i].invulnerable = self.player[i + 4].invulnerable self.player[i].hitlag_frames_left = self.player[ i + 4].hitlag_frames_left self.player[i].hitstun_frames_left = self.player[ i + 4].hitstun_frames_left self.player[i].charging_smash = self.player[ i + 4].charging_smash self.player[i].jumps_left = self.player[i + 4].jumps_left self.player[i].on_ground = self.player[i + 4].on_ground self.player[i].speed_air_x_self = self.player[ i + 4].speed_air_x_self self.player[i].speed_y_self = self.player[i + 4].speed_y_self self.player[i].speed_x_attack = self.player[ i + 4].speed_x_attack self.player[i].speed_y_attack = self.player[ i + 4].speed_y_attack self.player[i].speed_ground_x_self = self.player[ i + 4].speed_ground_x_self self.player[i].x = self.player[i + 4].x self.player[i].y = self.player[i + 4].y self.player[i].percent = self.player[i + 4].percent self.player[i].facing = self.player[i + 4].facing # The pre-warning occurs when we first start a dash dance. if self.player[i].action == Action.DASHING and self.player[ i].prev_action not in [Action.DASHING, Action.TURNING]: self.player[i].moonwalkwarning = True # Take off the warning if the player does an action other than dashing if self.player[i].action != Action.DASHING: self.player[i].moonwalkwarning = False #TODO: This needs updating in order to support >2 players xdist = self.ai_state.x - self.opponent_state.x ydist = self.ai_state.y - self.opponent_state.y self.distance = math.sqrt((xdist**2) + (ydist**2)) self.fixiasa() self.fixframeindexing() return True if label == "stage": self.stage = unpack('<I', mem_update[1])[0] self.stage = self.stage >> 16 self.stage &= 0x000000ff try: self.stage = enums.Stage(self.stage) except ValueError: self.stage = enums.Stage.NO_STAGE return False if label == "menu_state": self.menu_state = unpack('<I', mem_update[1])[0] self.menu_state &= 0x000000ff self.menu_state = enums.Menu(self.menu_state) return False #Player variables if label == "percent": if player_int > 4: try: self.player[player_int].percent = int( unpack('<f', mem_update[1])[0]) except ValueError: self.player[player_int].percent = 0 return False self.player[player_int].percent = unpack('<I', mem_update[1])[0] self.player[ player_int].percent = self.player[player_int].percent >> 16 return False if label == "stock": self.player[player_int].stock = unpack('<I', mem_update[1])[0] self.player[player_int].stock = self.player[player_int].stock >> 24 return False if label == "facing": self.player[player_int].facing = unpack('<I', mem_update[1])[0] self.player[player_int].facing = not bool( self.player[player_int].facing >> 31) return False if label == "x": self.player[player_int].next_x = unpack('<f', mem_update[1])[0] return False if label == "y": self.player[player_int].next_y = unpack('<f', mem_update[1])[0] return False if label == "character": temp = unpack('<I', mem_update[1])[0] >> 24 try: self.player[player_int].character = enums.Character(temp) except ValueError: self.player[ player_int].character = enums.Character.UNKNOWN_CHARACTER return False if label == "cursor_x": self.player[player_int].cursor_x = unpack('<f', mem_update[1])[0] return False if label == "cursor_y": self.player[player_int].cursor_y = unpack('<f', mem_update[1])[0] return False if label == "action": temp = unpack('<I', mem_update[1])[0] try: # Keep track of old action self.player[player_int].prev_action = self.player[ player_int].action self.player[player_int].action = enums.Action(temp) except ValueError: self.player[player_int].action = enums.Action.UNKNOWN_ANIMATION return False if label == "action_counter": #TODO look if this is backwards temp = unpack('I', mem_update[1])[0] temp = temp >> 8 self.player[player_int].action_counter = temp return False if label == "action_frame": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].action_frame = int(temp) except ValueError: pass return False if label == "invulnerable": self.player[player_int].invulnerable = unpack('<I', mem_update[1])[0] self.player[player_int].invulnerable = self.player[ player_int].invulnerable >> 31 return False if label == "hitlag_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitlag_frames_left = int(temp) except ValueError: pass return False if label == "hitstun_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitstun_frames_left = int(temp) except ValueError: pass return False if label == "charging_smash": temp = unpack('<I', mem_update[1])[0] if temp == 2: self.player[player_int].charging_smash = True else: self.player[player_int].charging_smash = False return False if label == "jumps_left": temp = unpack('<I', mem_update[1])[0] temp = temp >> 24 #This value is actually the number of jumps USED # so we have to do some quick math to turn this into what we want try: totaljumps = int(self.characterdata[ self.player[player_int].character]["Jumps"]) self.player[player_int].jumps_left = totaljumps - temp + 1 # Key error will be expected when we first start except KeyError: self.player[player_int].jumps_left = 1 return False if label == "on_ground": temp = unpack('<I', mem_update[1])[0] if temp == 0: self.player[player_int].on_ground = True else: self.player[player_int].on_ground = False return False if label == "speed_air_x_self": self.player[player_int].speed_air_x_self = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_self": self.player[player_int].speed_y_self = unpack('<f', mem_update[1])[0] return False if label == "speed_x_attack": self.player[player_int].speed_x_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_attack": self.player[player_int].speed_y_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_ground_x_self": self.player[player_int].speed_ground_x_self = unpack( '<f', mem_update[1])[0] return False if label == "coin_down": temp = unpack('<I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].coin_down = (temp == 2) return False if label == "stage_select_cursor_x": self.stage_select_cursor_x = unpack('<f', mem_update[1])[0] return False if label == "stage_select_cursor_y": self.stage_select_cursor_y = unpack('<f', mem_update[1])[0] return False if label == "ready_to_start": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.ready_to_start = not bool(temp) return False if label == "controller_status": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].controller_status = enums.ControllerStatus( temp) return False if label == "hitbox_1_size": self.player[player_int].hitbox_1_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_2_size": self.player[player_int].hitbox_2_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_3_size": self.player[player_int].hitbox_3_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_4_size": self.player[player_int].hitbox_4_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_1_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_1_status = status return False if label == "hitbox_2_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_2_status = status return False if label == "hitbox_3_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_3_status = status return False if label == "hitbox_4_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_4_status = status return False if label == "hitbox_1_x": self.player[player_int].hitbox_1_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_1_y": self.player[player_int].hitbox_1_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_x": self.player[player_int].hitbox_2_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_y": self.player[player_int].hitbox_2_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_x": self.player[player_int].hitbox_3_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_y": self.player[player_int].hitbox_3_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_x": self.player[player_int].hitbox_4_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_y": self.player[player_int].hitbox_4_y = unpack('<f', mem_update[1])[0] return False if label == "iasa": self.player[player_int].iasa = bool( unpack('<I', mem_update[1])[0] >> 31) return False if label == "transformed": temp = unpack('<I', mem_update[1])[0] status = False if temp == 16777216: status = True self.player[player_int].transformed = status return False if label == "iszelda": temp = unpack('<I', mem_update[1])[0] status = False if temp == 18: status = True self.player[player_int].iszelda = status return False if label == "projectiles": #Only once per new frame that we get a projectile, clear the list out if self.newframe: self.projectiles.clear() self.i = 0 self.i += 1 self.newframe = False if len(mem_update[1]) < 10: self.projectiles.clear() return False proj = Projectile() proj.x = unpack('>f', mem_update[1][0x4c:0x50])[0] proj.y = unpack('>f', mem_update[1][0x50:0x54])[0] proj.x_speed = unpack('>f', mem_update[1][0x40:0x44])[0] proj.y_speed = unpack('>f', mem_update[1][0x44:0x48])[0] try: proj.subtype = enums.ProjectileSubtype( unpack('>I', mem_update[1][0x10:0x14])[0]) except ValueError: return False self.projectiles.append(proj) return False
def update(self, mem_update): label = self.locations[mem_update[0]][0] player_int = int(self.locations[mem_update[0]][1]) if label == "frame": self.frame = unpack('<I', mem_update[1])[0] self.newframe = True #Now that the frame is ready, let's calculate some derived information # These are not stored inside Melee anywhere, but are nonetheless # important pieces of information that we don't want to make the # user have to re-calculate on their own for i in self.player: if abs(self.player[i].x) > stages.edgegroundposition( self.stage): self.player[i].off_stage = True else: self.player[i].off_stage = False #TODO: This needs updating in order to support >2 players xdist = self.ai_state.x - self.opponent_state.x ydist = self.ai_state.y - self.opponent_state.y self.distance = math.sqrt((xdist**2) + (ydist**2)) self.fixframeindexing() return True if label == "stage": self.stage = unpack('<I', mem_update[1])[0] self.stage = self.stage >> 16 self.stage &= 0x000000ff try: self.stage = enums.Stage(self.stage) except ValueError: self.stage = enums.Stage.NO_STAGE return False if label == "menu_state": self.menu_state = unpack('<I', mem_update[1])[0] self.menu_state &= 0x000000ff self.menu_state = enums.Menu(self.menu_state) return False #Player variables if label == "percent": self.player[player_int].percent = unpack('<I', mem_update[1])[0] self.player[ player_int].percent = self.player[player_int].percent >> 16 return False if label == "stock": self.player[player_int].stock = unpack('<I', mem_update[1])[0] self.player[player_int].stock = self.player[player_int].stock >> 24 return False if label == "facing": self.player[player_int].facing = unpack('<I', mem_update[1])[0] self.player[player_int].facing = not bool( self.player[player_int].facing >> 31) return False if label == "x": self.player[player_int].x = unpack('<f', mem_update[1])[0] return False if label == "y": self.player[player_int].y = unpack('<f', mem_update[1])[0] return False if label == "character": temp = unpack('<I', mem_update[1])[0] >> 24 try: self.player[player_int].character = enums.Character(temp) except ValueError: self.player[ player_int].character = enums.Character.UNKNOWN_CHARACTER return False if label == "cursor_x": self.player[player_int].cursor_x = unpack('<f', mem_update[1])[0] return False if label == "cursor_y": self.player[player_int].cursor_y = unpack('<f', mem_update[1])[0] return False if label == "action": temp = unpack('<I', mem_update[1])[0] try: self.player[player_int].action = enums.Action(temp) except ValueError: self.player[player_int].action = enums.Action.UNKNOWN_ANIMATION return False if label == "action_counter": #TODO look if this is backwards temp = unpack('I', mem_update[1])[0] temp = temp >> 8 self.player[player_int].action_counter = temp return False if label == "action_frame": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].action_frame = int(temp) except ValueError: pass return False if label == "invulnerable": self.player[player_int].invulnerable = unpack('<I', mem_update[1])[0] self.player[player_int].invulnerable = self.player[ player_int].invulnerable >> 31 return False if label == "hitlag_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitlag_frames_left = int(temp) except ValueError: pass return False if label == "hitstun_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitstun_frames_left = int(temp) except ValueError: pass return False if label == "charging_smash": temp = unpack('<I', mem_update[1])[0] if temp == 2: self.player[player_int].charging_smash = True else: self.player[player_int].charging_smash = False return False if label == "jumps_left": temp = unpack('<I', mem_update[1])[0] temp = temp >> 24 #This value is actually the number of jumps USED # so we have to do some quick math to turn this into what we want #TODO = characterstats.maxjumps(self.player[player_int].character) - temp + 1 self.player[player_int].jumps_left = temp return False if label == "on_ground": temp = unpack('<I', mem_update[1])[0] if temp == 0: self.player[player_int].on_ground = True else: self.player[player_int].on_ground = False return False if label == "speed_air_x_self": self.player[player_int].speed_air_x_self = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_self": self.player[player_int].speed_y_self = unpack('<f', mem_update[1])[0] return False if label == "speed_x_attack": self.player[player_int].speed_x_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_attack": self.player[player_int].speed_y_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_ground_x_self": self.player[player_int].speed_ground_x_self = unpack( '<f', mem_update[1])[0] return False if label == "coin_down": temp = unpack('<I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].coin_down = (temp == 2) return False if label == "stage_select_cursor_x": self.stage_select_cursor_x = unpack('<f', mem_update[1])[0] return False if label == "stage_select_cursor_y": self.stage_select_cursor_y = unpack('<f', mem_update[1])[0] return False if label == "ready_to_start": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.ready_to_start = not bool(temp) return False if label == "controller_status": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].controller_status = enums.ControllerStatus( temp) return False if label == "hitbox_1_size": self.player[player_int].hitbox_1_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_2_size": self.player[player_int].hitbox_2_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_3_size": self.player[player_int].hitbox_3_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_4_size": self.player[player_int].hitbox_4_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_1_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_1_status = status return False if label == "hitbox_2_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_2_status = status return False if label == "hitbox_3_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_3_status = status return False if label == "hitbox_4_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_4_status = status return False if label == "hitbox_1_x": self.player[player_int].hitbox_1_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_1_y": self.player[player_int].hitbox_1_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_x": self.player[player_int].hitbox_2_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_y": self.player[player_int].hitbox_2_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_x": self.player[player_int].hitbox_3_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_y": self.player[player_int].hitbox_3_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_x": self.player[player_int].hitbox_4_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_y": self.player[player_int].hitbox_4_y = unpack('<f', mem_update[1])[0] return False if label == "projectiles": #Only once per new frame that we get a projectile, clear the list out if self.newframe: self.projectiles.clear() self.i = 0 self.i += 1 self.newframe = False if len(mem_update[1]) < 10: self.projectiles.clear() return False proj = Projectile() proj.x = unpack('>f', mem_update[1][0x4c:0x50])[0] proj.y = unpack('>f', mem_update[1][0x50:0x54])[0] proj.x_speed = unpack('>f', mem_update[1][0x40:0x44])[0] proj.y_speed = unpack('>f', mem_update[1][0x44:0x48])[0] try: proj.subtype = enums.ProjectileSubtype( unpack('>I', mem_update[1][0x10:0x14])[0]) except ValueError: proj.subtype = enums.ProjectileSubtype.UNKNOWN_PROJECTILE self.projectiles.append(proj) return False
def __init__(self, write): if write: self.csvfile = open('framedata.csv', 'a') fieldnames = [ 'character', 'action', 'frame', 'hitbox_1_status', 'hitbox_1_size', 'hitbox_1_x', 'hitbox_1_y', 'hitbox_2_status', 'hitbox_2_size', 'hitbox_2_x', 'hitbox_2_y', 'hitbox_3_status', 'hitbox_3_size', 'hitbox_3_x', 'hitbox_3_y', 'hitbox_4_status', 'hitbox_4_size', 'hitbox_4_x', 'hitbox_4_y' ] self.writer = csv.DictWriter(self.csvfile, fieldnames=fieldnames) self.writer.writeheader() self.rows = [] #Read the existing framedata path = os.path.dirname(os.path.realpath(__file__)) self.framedata = defaultdict( lambda: defaultdict(lambda: defaultdict(dict))) with open(path + "/framedata.csv") as csvfile: # A list of dicts containing the frame data csvreader = list(csv.DictReader(csvfile)) # Build a series of nested dicts for faster read access for frame in csvreader: # Pull out the character, action, and frame character = enums.Character(int(frame["character"])) action = enums.Action(int(frame["action"])) action_frame = int(frame["frame"]) self.framedata[character][action][action_frame] = \ {"hitbox_1_status": frame["hitbox_1_status"] == "True", \ "hitbox_1_size": float(frame["hitbox_1_size"]), \ "hitbox_1_x": float(frame["hitbox_1_x"]), \ "hitbox_1_y": float(frame["hitbox_1_y"]), \ "hitbox_2_status": frame["hitbox_2_status"] == "True", \ "hitbox_2_size": float(frame["hitbox_2_size"]), \ "hitbox_2_x": float(frame["hitbox_2_x"]), \ "hitbox_2_y": float(frame["hitbox_2_y"]), \ "hitbox_3_status": frame["hitbox_3_status"] == "True", \ "hitbox_3_size": float(frame["hitbox_3_size"]), \ "hitbox_3_x": float(frame["hitbox_3_x"]), \ "hitbox_3_y": float(frame["hitbox_3_y"]), \ "hitbox_4_status": frame["hitbox_4_status"] == "True", \ "hitbox_4_size": float(frame["hitbox_4_size"]), \ "hitbox_4_x": float(frame["hitbox_4_x"]), \ "hitbox_4_y": float(frame["hitbox_4_y"])} #Read the action state data csv path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/actiondata.csv") as csvfile: # A list of dicts containing the frame data self.actiondata = list(csv.DictReader(csvfile)) #read the character data csv self.characterdata = dict() path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/characterdata.csv") as csvfile: reader = csv.DictReader(csvfile) for line in reader: del line["Character"] #Convert all fields to numbers for key, value in line.items(): line[key] = float(value) self.characterdata[enums.Character( line["CharacterIndex"])] = line
def __post_frame(self, gamestate, event_bytes): gamestate.stage = self._current_stage gamestate.frame = np.ndarray((1,), ">i", event_bytes, 0x1)[0] controller_port = np.ndarray((1,), ">B", event_bytes, 0x5)[0] + 1 if controller_port not in gamestate.player: gamestate.player[controller_port] = PlayerState() playerstate = gamestate.player[controller_port] playerstate.x = np.ndarray((1,), ">f", event_bytes, 0xa)[0] playerstate.y = np.ndarray((1,), ">f", event_bytes, 0xe)[0] playerstate.character = enums.Character(np.ndarray((1,), ">B", event_bytes, 0x7)[0]) try: playerstate.action = enums.Action(np.ndarray((1,), ">H", event_bytes, 0x8)[0]) except ValueError: playerstate.action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert playerstate.facing = np.ndarray((1,), ">f", event_bytes, 0x12)[0] > 0 playerstate.percent = int(np.ndarray((1,), ">f", event_bytes, 0x16)[0]) playerstate.shield_strength = np.ndarray((1,), ">f", event_bytes, 0x1A)[0] playerstate.stock = np.ndarray((1,), ">B", event_bytes, 0x21)[0] playerstate.action_frame = int(np.ndarray((1,), ">f", event_bytes, 0x22)[0]) # Extract the bit at mask 0x20 try: bitflags2 = np.ndarray((1,), ">B", event_bytes, 0x27)[0] playerstate.hitlag = bool(bitflags2 & 0x20) except TypeError: playerstate.hitlag = False try: playerstate.hitstun_frames_left = int(np.ndarray((1,), ">f", event_bytes, 0x2B)[0]) except TypeError: playerstate.hitstun_frames_left = 0 except ValueError: playerstate.hitstun_frames_left = 0 try: playerstate.on_ground = not bool(np.ndarray((1,), ">B", event_bytes, 0x2F)[0]) except TypeError: playerstate.on_ground = True try: playerstate.jumps_left = np.ndarray((1,), ">B", event_bytes, 0x32)[0] except TypeError: playerstate.jumps_left = 1 try: playerstate.invulnerable = int(np.ndarray((1,), ">B", event_bytes, 0x34)[0]) != 0 except TypeError: playerstate.invulnerable = False try: playerstate.speed_air_x_self = np.ndarray((1,), ">f", event_bytes, 0x35)[0] except TypeError: playerstate.speed_air_x_self = 0 try: playerstate.speed_y_self = np.ndarray((1,), ">f", event_bytes, 0x39)[0] except TypeError: playerstate.speed_y_self = 0 try: playerstate.speed_x_attack = np.ndarray((1,), ">f", event_bytes, 0x3D)[0] except TypeError: playerstate.speed_x_attack = 0 try: playerstate.speed_y_attack = np.ndarray((1,), ">f", event_bytes, 0x41)[0] except TypeError: playerstate.speed_y_attack = 0 try: playerstate.speed_ground_x_self = np.ndarray((1,), ">f", event_bytes, 0x45)[0] except TypeError: playerstate.speed_ground_x_self = 0 # Keep track of a player's invulnerability due to respawn or ledge grab if controller_port in self._prev_gamestate.player: playerstate.invulnerability_left = max(0, self._prev_gamestate.player[controller_port].invulnerability_left - 1) if playerstate.action == Action.ON_HALO_WAIT: playerstate.invulnerability_left = 120 # Don't give invulnerability to the first descent if playerstate.action == Action.ON_HALO_DESCENT and gamestate.frame > 150: playerstate.invulnerability_left = 120 if playerstate.action == Action.EDGE_CATCHING and playerstate.action_frame == 1: playerstate.invulnerability_left = 36 # The pre-warning occurs when we first start a dash dance. if controller_port in self._prev_gamestate.player: if playerstate.action == Action.DASHING and \ self._prev_gamestate.player[controller_port].action not in [Action.DASHING, Action.TURNING]: playerstate.moonwalkwarning = True # Take off the warning if the player does an action other than dashing if playerstate.action != Action.DASHING: playerstate.moonwalkwarning = False # "off_stage" helper if (abs(playerstate.x) > stages.EDGE_GROUND_POSITION[gamestate.stage] or \ playerstate.y < -6) and not playerstate.on_ground: playerstate.off_stage = True else: playerstate.off_stage = False # ECB top edge, x ecb_top_x = 0 ecb_top_y = 0 try: ecb_top_x = np.ndarray((1,), ">f", event_bytes, 0x49)[0] except TypeError: ecb_top_x = 0 # ECB Top edge, y try: ecb_top_y = np.ndarray((1,), ">f", event_bytes, 0x4D)[0] except TypeError: ecb_top_y = 0 playerstate.ecb_top = (ecb_top_x, ecb_top_y) # ECB bottom edge, x coord ecb_bot_x = 0 ecb_bot_y = 0 try: ecb_bot_x = np.ndarray((1,), ">f", event_bytes, 0x51)[0] except TypeError: ecb_bot_x = 0 # ECB Bottom edge, y coord try: ecb_bot_y = np.ndarray((1,), ">f", event_bytes, 0x55)[0] except TypeError: ecb_bot_y = 0 playerstate.ecb_bottom = (ecb_bot_x, ecb_bot_y) # ECB left edge, x coord ecb_left_x = 0 ecb_left_y = 0 try: ecb_left_x = np.ndarray((1,), ">f", event_bytes, 0x59)[0] except TypeError: ecb_left_x = 0 # ECB left edge, y coord try: ecb_left_y = np.ndarray((1,), ">f", event_bytes, 0x5D)[0] except TypeError: ecb_left_y = 0 playerstate.ecb_left = (ecb_left_x, ecb_left_y) # ECB right edge, x coord ecb_right_x = 0 ecb_right_y = 0 try: ecb_right_x = np.ndarray((1,), ">f", event_bytes, 0x61)[0] except TypeError: ecb_right_x = 0 # ECB right edge, y coord try: ecb_right_y = np.ndarray((1,), ">f", event_bytes, 0x65)[0] except TypeError: ecb_right_y = 0 playerstate.ecb_right = (ecb_right_x, ecb_right_y) if self._allow_old_version: self._frame = gamestate.frame