def clean_monster_behavior(o: MonsterBehavior) -> MonsterBehavior: r = MonsterBehavior() r.monster_id = o.monster_id r.approved = o.approved for l in o.levels: r.levels.append(clean_level_behavior(l)) return r
def load_enemy_data(self, base_dir: str): card_files = [] logger.warning('scanning enemy data for %d cards', len(self.data.all_cards)) for csc in self.data.all_cards: card_file = os.path.join(base_dir, '{}.textproto'.format(csc.monster_id)) if not os.path.exists(card_file): continue card_files.append(card_file) logger.warning('loading enemy data for %d cards', len(card_files)) count_not_approved = 0 count_approved = 0 for card_file in card_files: mbwo = enemy_skill_proto.load_from_file(card_file) mb = MonsterBehavior() mb.monster_id = mbwo.monster_id if mbwo.status == enemy_skills_pb2.MonsterBehaviorWithOverrides.NOT_APPROVED: mb.levels.extend(mbwo.levels) mb.approved = False count_not_approved += 1 else: mb.levels.extend(mbwo.level_overrides) mb.approved = True count_approved += 1 item = EnemyData.from_mb(mb) self.db.insert_or_update(item) logger.warning('done, %d approved %d not approved', count_approved, count_not_approved)
def process_card(csc: CrossServerCard) -> MonsterBehavior: enemy_behavior = [x.na_skill for x in csc.enemy_behavior] card = csc.na_card.card if not enemy_behavior: return None inject_implicit_onetime(card, enemy_behavior) levels = enemy_skillset_processor.extract_levels(enemy_behavior) skill_listings = [] used_actions = [] for level in sorted(levels): try: skillset = enemy_skillset_processor.convert(card, enemy_behavior, level) if not skillset.has_actions(): continue flattened = enemy_skill_proto.flatten_skillset(level, skillset) used_actions.extend(debug_utils.extract_used_skills(skillset)) skill_listings.append(flattened) except Exception as ex: if 'No loop' not in str(ex): raise ex else: # TODO: some monsters have whacked out behavior (they aren't real monsters) # Should start ignoring those (e.g. pixel yuna). print('\tLoop detection failure for', card.monster_no, card.name) if not skill_listings: return None unused_actions = [] for b in enemy_behavior: try: if issubclass(b.btype, ESAction) and b not in used_actions and b not in unused_actions: unused_actions.append(b) except: print('oops') # TODO: add unused actions and store them result = MonsterBehavior() result.monster_id = csc.monster_id result.levels.extend(skill_listings) return result
def process_card(csc: CrossServerCard) -> MonsterBehavior: enemy_behavior = [x.na_skill for x in csc.enemy_behavior] card = csc.na_card.card if not enemy_behavior: return None levels = enemy_skillset_processor.extract_levels(enemy_behavior) skill_listings = [] # type: List[LevelBehavior] seen_level_behavior = set() # type: Set[str] used_actions = [] # type: List[ESInstance] for level in sorted(levels): try: skillset = enemy_skillset_processor.convert( card, enemy_behavior, level) if not skillset.has_actions(): continue flattened = enemy_skill_proto.flatten_skillset(level, skillset) # Check if we've already seen this level behavior; zero out the level and stick it # in a set containing all the levels we've seen. We want the behavior set at the # lowest possible level. zerod_flattened = LevelBehavior() zerod_flattened.CopyFrom(flattened) zerod_flattened.level = 0 zerod_value = zerod_flattened.SerializeToString() if zerod_value in seen_level_behavior: continue else: seen_level_behavior.add(zerod_value) used_actions.extend(debug_utils.extract_used_skills(skillset)) skill_listings.append(flattened) except Exception as ex: if 'No loop' not in str(ex): raise ex else: # TODO: some monsters have whacked out behavior (they aren't real monsters) # Should start ignoring those (e.g. pixel yuna). print('\tLoop detection failure for', card.monster_no, card.name) break if not skill_listings: return None unused_actions = [] for b in enemy_behavior: try: is_action = isinstance(b.behavior, ESAction) is_used = b not in used_actions already_in_unused = b not in unused_actions is_death_action = isinstance(b.behavior, ESDeathAction) if is_action and is_used and already_in_unused and not is_death_action: unused_actions.append(b) except: print('oops') for level_behavior in skill_listings: add_unused(unused_actions, level_behavior) result = MonsterBehavior() result.monster_id = csc.monster_id result.levels.extend(skill_listings) return result
def process_card(csc: CrossServerCard) -> MonsterBehavior: enemy_behavior = [x.na_skill for x in csc.enemy_behavior] # Apply conditional overrides cond_overrides = CONDITIONAL_OVERRIDES.get(csc.monster_id, []) + CONDITIONAL_OVERRIDES[0] uncond_overrides = UNCONDITIONAL_OVERRIDES.get( csc.monster_id, []) + UNCONDITIONAL_OVERRIDES[0] for eb in enemy_behavior: if eb.enemy_skill_id in cond_overrides: eb.behavior.conditional = True if eb.enemy_skill_id in uncond_overrides: eb.behavior.conditional = False card = csc.na_card.card if not enemy_behavior: return None # Override the NA increment and max counter from the JP version. # This can rarely be different, typically when a collab comes to NA/JP # in the first run, and then gets updated in JP later. # TODO: Possibly this should occur in the merged card # card.enemy_skill_max_counter = max(card.enemy_skill_max_counter, # csc.jp_card.card.enemy_skill_max_counter) # card.enemy_skill_counter_increment = max(card.enemy_skill_counter_increment, # csc.jp_card.card.enemy_skill_counter_increment) # TODO: That wasn't always correct; probably needs to use the value from whichever ES tree is selected card.enemy_skill_max_counter = csc.jp_card.card.enemy_skill_max_counter card.enemy_skill_counter_increment = csc.jp_card.card.enemy_skill_counter_increment levels = enemy_skillset_processor.extract_levels(enemy_behavior) long_loop = csc.monster_id in LONG_LOOP_MONSTERS skill_listings = [] # type: List[LevelBehavior] previous_level_behavior = "" used_actions = [] # type: List[ESInstance] for level in sorted(levels): try: skillset = enemy_skillset_processor.convert( card, enemy_behavior, level, long_loop) if not skillset.has_actions(): continue skillset_extraction = (csc.monster_id % 100000) in APPLY_SKILLSET_GROUPING flattened = enemy_skill_proto.flatten_skillset( level, skillset, skillset_extraction=skillset_extraction) # Check if we've already seen this level behavior; zero out the level and stick it # in a set containing all the levels we've seen. We want the behavior set at the # lowest possible level. # # However, some monsters have a weird alternating ES behavior (1/3/5 are the same, # 2/4/6 are the same) so to deal with that we only compare against the last seen. zerod_flattened = LevelBehavior() zerod_flattened.CopyFrom(flattened) zerod_flattened.level = 0 zerod_value = zerod_flattened.SerializeToString() if zerod_value == previous_level_behavior: continue else: previous_level_behavior = zerod_value used_actions.extend(debug_utils.extract_used_skills(skillset)) skill_listings.append(flattened) except Exception as ex: if 'No loop' not in str(ex): raise ex else: # TODO: some monsters have whacked out behavior (they aren't real monsters) # Should start ignoring those (e.g. pixel yuna). print('\tLoop detection failure for', card.monster_no, card.name) break if not skill_listings: return None unused_actions = [] for b in enemy_behavior: try: is_action = isinstance(b.behavior, ESAction) is_used = b not in used_actions already_in_unused = b not in unused_actions is_death_action = isinstance(b.behavior, ESDeathAction) if is_action and is_used and already_in_unused and not is_death_action: unused_actions.append(b) except: print('oops') for level_behavior in skill_listings: add_unused(unused_actions, level_behavior) result = MonsterBehavior() result.monster_id = csc.monster_id result.levels.extend(skill_listings) return result
def from_mb(o: MonsterBehavior, status: int) -> 'EnemyData': return EnemyData(enemy_id=o.monster_id, status=status, behavior=o.SerializeToString())
def process_card(csc: CrossServerCard) -> MonsterBehavior: enemy_behavior = [x.na_skill for x in csc.enemy_behavior] card = csc.na_card.card if not enemy_behavior: return None # Override the NA increment and max counter from the JP version. # This can rarely be different, typically when a collab comes to NA/JP # in the first run, and then gets updated in JP later. # TODO: Possibly this should occur in the merged card card.enemy_skill_max_counter = max( card.enemy_skill_max_counter, csc.jp_card.card.enemy_skill_max_counter) card.enemy_skill_counter_increment = max( card.enemy_skill_counter_increment, csc.jp_card.card.enemy_skill_counter_increment) levels = enemy_skillset_processor.extract_levels(enemy_behavior) long_loop = csc.monster_id in LONG_LOOP_MONSTERS skill_listings = [] # type: List[LevelBehavior] seen_level_behavior = set() # type: Set[str] used_actions = [] # type: List[ESInstance] for level in sorted(levels): try: skillset = enemy_skillset_processor.convert( card, enemy_behavior, level, long_loop) if not skillset.has_actions(): continue flattened = enemy_skill_proto.flatten_skillset(level, skillset) # Check if we've already seen this level behavior; zero out the level and stick it # in a set containing all the levels we've seen. We want the behavior set at the # lowest possible level. zerod_flattened = LevelBehavior() zerod_flattened.CopyFrom(flattened) zerod_flattened.level = 0 zerod_value = zerod_flattened.SerializeToString() if zerod_value in seen_level_behavior: continue else: seen_level_behavior.add(zerod_value) used_actions.extend(debug_utils.extract_used_skills(skillset)) skill_listings.append(flattened) except Exception as ex: if 'No loop' not in str(ex): raise ex else: # TODO: some monsters have whacked out behavior (they aren't real monsters) # Should start ignoring those (e.g. pixel yuna). print('\tLoop detection failure for', card.monster_no, card.name) break if not skill_listings: return None unused_actions = [] for b in enemy_behavior: try: is_action = isinstance(b.behavior, ESAction) is_used = b not in used_actions already_in_unused = b not in unused_actions is_death_action = isinstance(b.behavior, ESDeathAction) if is_action and is_used and already_in_unused and not is_death_action: unused_actions.append(b) except: print('oops') for level_behavior in skill_listings: add_unused(unused_actions, level_behavior) result = MonsterBehavior() result.monster_id = csc.monster_id result.levels.extend(skill_listings) return result
def from_mb(o: MonsterBehavior) -> 'EnemyData': return EnemyData(enemy_id=o.monster_id, behavior=o.SerializeToString())