def get_actions_for_names(names): """Returns a list of actions granted by the list of feature names. Will filter out any duplicates.""" actions = [] seen_action_names = set() for name in names: g_actions = get_actions_for_name(name) # in some cases, a very generic feature name (e.g. "Channel Divinity") will grant far more actions than we want # code snippet to determine this threshold: # bleps = [(name, len(actions), actions) for name, actions in discoverer.actions_granted_by_name.items()] # bleps = sorted(bleps, key=lambda blep: blep[1], reverse=True) if len(g_actions) > 20: continue for g_action in g_actions: if g_action.name in seen_action_names: continue seen_action_names.add(g_action.name) actions.append( Action(name=g_action.name, uid=g_action.uid, id=g_action.id, type_id=g_action.type_id, activation_type=g_action.activation_type)) return actions
def add_action_from_gamedata(d_action, g_action): name = g_action.name if d_action.get( 'isCustomized') and d_action['name'] != g_action.name: # custom name handler: steal any parenthetical from the canonical name and tack it on to the custom name name = d_action['name'] g_name_parenthetical = re.search(r'\(.+\)$', g_action.name) if g_name_parenthetical: name = f"{name} {g_name_parenthetical.group(0)}" actions.append( Action(name=name, uid=g_action.uid, id=g_action.id, type_id=g_action.type_id, activation_type=g_action.activation_type, snippet=html_to_md(d_action['snippet'])))
def get_actions(self): # iterate over features and look for actions with the same name, snippet is the feature description? actions = [] g_actions_by_name = {a.name: a for a in compendium.actions} for f in self.character_data.get('features', []): if not f.get('enabled'): continue if f.get('removed'): continue name = f.get('name') if name not in g_actions_by_name: continue g_action = g_actions_by_name[name] actions.append(Action( name=g_action.name, uid=g_action.uid, id=g_action.id, type_id=g_action.type_id, activation_type=g_action.activation_type, snippet=f.get('description') )) return Actions(actions)
def get_actions(self): # iterate over features and look for actions with the same name actions = [] g_actions_by_name = {a.name: a for a in compendium.actions} # v1: Z45:AH56 # v2: C59:AC84 if self.version >= (2, 0): feature_names = self.character_data.value_range("C59:AC84") else: feature_names = self.character_data.value_range("Z45:AH56") for name in feature_names: if name not in g_actions_by_name: continue g_action = g_actions_by_name[name] actions.append( Action(name=g_action.name, uid=g_action.uid, id=g_action.id, type_id=g_action.type_id, activation_type=g_action.activation_type)) return Actions(actions)
def _process_actions(self): """ :return: a pair (actions, action ids that granted valid actions (id, typeid)) :rtype: tuple[Actions, set[tuple[str, str]]] """ character_actions = self.character_data['actions'] character_features = self.character_data['features'] actions = [] seen_feature_ids = set() # set of tuples (typeid, id) action_ids_with_valid_actions = set() # set of tuples (id, typeid) def add_action_from_gamedata(d_action, g_action): name = g_action.name if d_action.get( 'isCustomized') and d_action['name'] != g_action.name: # custom name handler: steal any parenthetical from the canonical name and tack it on to the custom name name = d_action['name'] g_name_parenthetical = re.search(r'\(.+\)$', g_action.name) if g_name_parenthetical: name = f"{name} {g_name_parenthetical.group(0)}" actions.append( Action(name=name, uid=g_action.uid, id=g_action.id, type_id=g_action.type_id, activation_type=g_action.activation_type, snippet=html_to_md(d_action['snippet']))) # actions: save all, regardless of gamedata presence for d_action in character_actions: # Unarmed Strike - already in attacks if d_action['typeId'] == '1120657896' and d_action['id'] == '1': continue # display as attack override - fall back to attack system if d_action['isCustomized'] and d_action['displayAsAttack']: continue # racial feature affected by martial arts - fall back to attack system if (d_action['isMartialArts'] and d_action['displayAsAttack'] and int(d_action['componentTypeId']) == gamedata.race.RaceFeature.type_id): continue # gamedata for limiteduse try: g_actions = compendium.lookup_actions_for_entity( int(d_action['typeId']), int(d_action['id'])) except ( TypeError, ValueError ): # weird null typeid/uuid id action, maybe artificer infused item? continue # gamedata for component (parent feature) # data might have been entered for the parent feature instead try: parent_type_id, parent_id = int( d_action['componentTypeId']), int(d_action['componentId']) parent_g_actions = compendium.lookup_actions_for_entity( parent_type_id, parent_id) seen_feature_ids.add((parent_type_id, parent_id)) except (TypeError, ValueError): parent_g_actions = [] # if the lu itself has action data, save a reference to each gamedata action by UID for g_action in itertools.chain(g_actions, parent_g_actions): add_action_from_gamedata(d_action, g_action) # just save the action w/ its snippet if there is no gamedata if not (g_actions or parent_g_actions): activation_type = (enums.ActivationType( d_action['activationType']) if d_action['activationType'] is not None else None) actions.append( Action(name=d_action['name'], uid=None, id=int(d_action['id']), type_id=int(d_action['typeId']), activation_type=activation_type, snippet=html_to_md(d_action['snippet']))) else: # otherwise, either it or its parent successfully added something, so we can save its id # for attack filtering action_ids_with_valid_actions.add( (str(d_action['id']), str(d_action['typeId']))) # features: save only if gamedata references them for d_feature in character_features: d_type_id, d_id = int(d_feature['typeId']), int(d_feature['id']) if (d_type_id, d_id) in seen_feature_ids: continue g_actions = compendium.lookup_actions_for_entity(d_type_id, d_id) for g_action in g_actions: add_action_from_gamedata(d_feature, g_action) return Actions(actions), action_ids_with_valid_actions