# generated by update to not change manually
import dataclasses as dt
import typing as t
from enum import Enum
from bungieapi.json import to_json
[docs]@dt.dataclass(frozen=True)
class DestinyDefinition:
"""Provides common properties for destiny definitions."""
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyProgressionDefinition:
"""A "Progression" in Destiny is best explained by an example.
A Character's "Level" is a progression: it has Experience that can
be earned, levels that can be gained, and is evaluated and displayed
at various points in the game. A Character's "Faction Reputation" is
also a progression for much the same reason. Progression is used by
a variety of systems, and the definition of a Progression will
generally only be useful if combining with live data (such as a
character's DestinyCharacterProgressionComponent.progressions
property, which holds that character's live Progression states).
Fundamentally, a Progression measures your "Level" by evaluating the
thresholds in its Steps (one step per level, except for the last
step which can be repeated indefinitely for "Levels" that have no
ceiling) against the total earned "progression points"/experience.
(for simplicity purposes, we will henceforth refer to earned
progression points as experience, though it need not be a mechanic
that in any way resembles Experience in a traditional sense). Earned
experience is calculated in a variety of ways, determined by the
Progression's scope. These go from looking up a stored value to
performing exceedingly obtuse calculations. This is why we provide
live data in DestinyCharacterProgressionComponent.progressions, so
you don't have to worry about those.
"""
color: t.Optional[
"DestinyColor"
] = None # The #RGB string value for the color related to this progression, if there is one.
display_properties: t.Optional[
"DestinyProgressionDisplayPropertiesDefinition"
] = None
faction_hash: t.Optional[
int
] = None # If the value exists, this is the hash identifier for the Faction that owns this Progression. This is purely for convenience, if you're looking at a progression and want to know if and who it's related to in terms of Faction Reputation.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
rank_icon: t.Optional[
str
] = None # For progressions that have it, this is the rank icon we use in the Companion, displayed above the progressions' rank value.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
repeat_last_step: t.Optional[
bool
] = None # If this is True, then the progression doesn't have a maximum level.
reward_items: t.Optional[t.Sequence["DestinyProgressionRewardItemQuantity"]] = None
scope: t.Optional[
"DestinyProgressionScope"
] = None # The "Scope" of the progression indicates the source of the progression's live data. See the DestinyProgressionScope enum for more info: but essentially, a Progression can either be backed by a stored value, or it can be a calculated derivative of other values.
source: t.Optional[
str
] = None # If there's a description of how to earn this progression in the local config, this will be that localized description.
steps: t.Optional[
t.Sequence["DestinyProgressionStepDefinition"]
] = None # Progressions are divided into Steps, which roughly equate to "Levels" in the traditional sense of a Progression. Notably, the last step can be repeated indefinitely if repeatLastStep is true, meaning that the calculation for your level is not as simple as comparing your current progress to the max progress of the steps. These and more calculations are done for you if you grab live character progression data, such as in the DestinyCharacterProgressionComponent.
visible: t.Optional[
bool
] = None # If true, the Progression is something worth showing to users. If false, BNet isn't going to show it. But that doesn't mean you can't. We're all friends here.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"scope": to_json(self.scope),
"repeatLastStep": to_json(self.repeat_last_step),
"source": to_json(self.source),
"steps": to_json(self.steps),
"visible": to_json(self.visible),
"factionHash": to_json(self.faction_hash),
"color": to_json(self.color),
"rankIcon": to_json(self.rank_icon),
"rewardItems": to_json(self.reward_items),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyProgressionDisplayPropertiesDefinition:
description: t.Optional[str] = None
display_units_name: t.Optional[
str
] = None # When progressions show your "experience" gained, that bar has units (i.e. "Experience", "Bad Dudes Snuffed Out", whatever). This is the localized string for that unit of measurement.
has_icon: t.Optional[bool] = None
high_res_icon: t.Optional[
str
] = None # If this item has a high-res icon (at least for now, many things won't), then the path to that icon will be here.
icon: t.Optional[
str
] = None # Note that "icon" is sometimes misleading, and should be interpreted in the context of the entity. For instance, in Destiny 1 the DestinyRecordBookDefinition's icon was a big picture of a book. But usually, it will be a small square image that you can use as... well, an icon. They are currently represented as 96px x 96px images.
icon_sequences: t.Optional[t.Sequence["DestinyIconSequenceDefinition"]] = None
name: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayUnitsName": to_json(self.display_units_name),
"description": to_json(self.description),
"name": to_json(self.name),
"icon": to_json(self.icon),
"iconSequences": to_json(self.icon_sequences),
"highResIcon": to_json(self.high_res_icon),
"hasIcon": to_json(self.has_icon),
}
[docs]@dt.dataclass(frozen=True)
class DestinyProgressionStepDefinition:
"""This defines a single Step in a progression (which roughly equates to a
level.
See DestinyProgressionDefinition for caveats).
"""
display_effect_type: t.Optional[
"DestinyProgressionStepDisplayEffect"
] = None # This appears to be, when you "level up", whether a visual effect will display and on what entity. See DestinyProgressionStepDisplayEffect for slightly more info.
icon: t.Optional[
str
] = None # If this progression step has a specific icon related to it, this is the icon to show.
progress_total: t.Optional[
int
] = None # The total amount of progression points/"experience" you will need to initially reach this step. If this is the last step and the progression is repeating indefinitely (DestinyProgressionDefinition.repeatLastStep), this will also be the progress needed to level it up further by repeating this step again.
reward_items: t.Optional[
t.Sequence["DestinyItemQuantity"]
] = None # A listing of items rewarded as a result of reaching this level.
step_name: t.Optional[
str
] = None # Very rarely, Progressions will have localized text describing the Level of the progression. This will be that localized text, if it exists. Otherwise, the standard appears to be to simply show the level numerically.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"stepName": to_json(self.step_name),
"displayEffectType": to_json(self.display_effect_type),
"progressTotal": to_json(self.progress_total),
"rewardItems": to_json(self.reward_items),
"icon": to_json(self.icon),
}
[docs]@dt.dataclass(frozen=True)
class DestinyInventoryItemDefinition:
"""So much of what you see in Destiny is actually an Item used in a new and
creative way.
This is the definition for Items in Destiny, which started off as
just entities that could exist in your Inventory but ended up being
the backing data for so much more: quests, reward previews, slots,
and subclasses. In practice, you will want to associate this data
with "live" item data from a Bungie.Net Platform call: these
definitions describe the item in generic, non-instanced terms: but
an actual instance of an item can vary widely from these generic
definitions.
"""
action: t.Optional[
"DestinyItemActionBlockDefinition"
] = None # If the item can be "used", this block will be non-null, and will have data related to the action performed when using the item. (Guess what? 99% of the time, this action is "dismantle". Shocker)
allow_actions: t.Optional[
bool
] = None # BNet may forbid the execution of actions on this item via the API. If that is occurring, allowActions will be set to false.
animations: t.Optional[
t.Sequence["DestinyAnimationReference"]
] = None # If any animations were extracted from game content for this item, these will be the definitions of those animations.
background_color: t.Optional[
"DestinyColor"
] = None # Sometimes, an item will have a background color. Most notably this occurs with Emblems, who use the Background Color for small character nameplates such as the "friends" view you see in-game. There are almost certainly other items that have background color as well, though I have not bothered to investigate what items have it nor what purposes they serve: use it as you will.
breaker_type: t.Optional[
"DestinyBreakerType"
] = None # Some weapons and plugs can have a "Breaker Type": a special ability that works sort of like damage type vulnerabilities. This is (almost?) always set on items by plugs.
breaker_type_hash: t.Optional[
int
] = None # Since we also have a breaker type definition, this is the hash for that breaker type for your convenience. Whether you use the enum or hash and look up the definition depends on what's cleanest for your code.
class_type: t.Optional[
"DestinyClass"
] = None # We run a similarly weak-sauce algorithm to try and determine whether an item is restricted to a specific class. If we find it to be restricted in such a way, we set this classType property to match the class' enumeration value so that users can easily identify class restricted items. If you see a mis-classed item, please inform the developers in the Bungie API forum.
collectible_hash: t.Optional[
int
] = None # If this item has a collectible related to it, this is the hash identifier of that collectible entry.
damage_type_hashes: t.Optional[
t.Sequence[int]
] = None # Theoretically, an item can have many possible damage types. In *practice*, this is not true, but just in case weapons start being made that have multiple (for instance, an item where a socket has reusable plugs for every possible damage type that you can choose from freely), this field will return all of the possible damage types that are available to the weapon by default.
damage_types: t.Optional[
t.Sequence["DamageType"]
] = None # This is the list of all damage types that we know ahead of time the item can take on. Unfortunately, this does not preclude the possibility of something funky happening to give the item a damage type that cannot be predicted beforehand: for example, if some designer decides to create arbitrary non-reusable plugs that cause damage type to change. This damage type prediction will only use the following to determine potential damage types: - Intrinsic perks - Talent Node perks - Known, reusable plugs for sockets
default_damage_type: t.Optional[
"DamageType"
] = None # If the item has a damage type that could be considered to be default, it will be populated here. For various upsetting reasons, it's surprisingly cumbersome to figure this out. I hope you're happy.
default_damage_type_hash: t.Optional[
int
] = None # Similar to defaultDamageType, but represented as the hash identifier for a DestinyDamageTypeDefinition. I will likely regret leaving in the enumeration versions of these properties, but for now they're very convenient.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
display_source: t.Optional[
str
] = None # In theory, it is a localized string telling you about how you can find the item. I really wish this was more consistent. Many times, it has nothing. Sometimes, it's instead a more narrative-forward description of the item. Which is cool, and I wish all properties had that data, but it should really be its own property.
does_postmaster_pull_have_side_effects: t.Optional[
bool
] = None # The boolean will indicate to us (and you!) whether something *could* happen when you transfer this item from the Postmaster that might be considered a "destructive" action. It is not feasible currently to tell you (or ourelves!) in a consistent way whether this *will* actually cause a destructive action, so we are playing it safe: if it has the potential to do so, we will not allow it to be transferred from the Postmaster by default. You will need to check for this flag before transferring an item from the Postmaster, or else you'll end up receiving an error.
emblem_objective_hash: t.Optional[
int
] = None # If the item is an emblem that has a special Objective attached to it - for instance, if the emblem tracks PVP Kills, or what-have-you. This is a bit different from, for example, the Vanguard Kill Tracker mod, which pipes data into the "art channel". When I get some time, I would like to standardize these so you can get at the values they expose without having to care about what they're being used for and how they are wired up, but for now here's the raw data.
equippable: t.Optional[
bool
] = None # If true, then you will be allowed to equip the item if you pass its other requirements. This being false means that you cannot equip the item under any circumstances.
equipping_block: t.Optional[
"DestinyEquippingBlockDefinition"
] = None # If this item can be equipped, this block will be non-null and will be populated with the conditions under which it can be equipped.
flavor_text: t.Optional[str] = None
gearset: t.Optional[
"DestinyItemGearsetBlockDefinition"
] = None # If this item has related items in a "Gear Set", this will be non-null and the relationships defined herein.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
icon_watermark: t.Optional[
str
] = None # If available, this is the original 'active' release watermark overlay for the icon. If the item has different versions, this can be overridden by the 'display version watermark icon' from the 'quality' block. Alternatively, if there is no watermark for the version, and the item version has a power cap below the current season power cap, this can be overridden by the iconWatermarkShelved property.
icon_watermark_shelved: t.Optional[
str
] = None # If available, this is the 'shelved' release watermark overlay for the icon. If the item version has a power cap below the current season power cap, it can be treated as 'shelved', and should be shown with this 'shelved' watermark overlay.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
inventory: t.Optional[
"DestinyItemInventoryBlockDefinition"
] = None # If this item can exist in an inventory, this block will be non-null. In practice, every item that currently exists has one of these blocks. But note that it is not necessarily guaranteed.
investment_stats: t.Optional[
t.Sequence["DestinyItemInvestmentStatDefinition"]
] = None # If the item has stats, this block will be defined. It has the "raw" investment stats for the item. These investment stats don't take into account the ways that the items can spawn, nor do they take into account any Stat Group transformations. I have retained them for debugging purposes, but I do not know how useful people will find them.
is_wrapper: t.Optional[
bool
] = None # If true, this is a dummy vendor-wrapped item template. Items purchased from Eververse will be "wrapped" by one of these items so that we can safely provide refund capabilities before the item is "unwrapped".
item_category_hashes: t.Optional[
t.Sequence[int]
] = None # BNet attempts to make a more formal definition of item "Categories", as defined by DestinyItemCategoryDefinition. This is a list of all Categories that we were able to algorithmically determine that this item is a member of. (for instance, that it's a "Weapon", that it's an "Auto Rifle", etc...) The algorithm for these is, unfortunately, volatile. If you believe you see a miscategorized item, please let us know on the Bungie API forums.
item_sub_type: t.Optional[
"DestinyItemSubType"
] = None # A value indicating the "sub-type" of the item. For instance, where an item might have an itemType value "Weapon", this will be something more specific like "Auto Rifle". itemCategoryHashes are the preferred way of identifying types, we have retained this enum for its convenience.
item_type: t.Optional[
"DestinyItemType"
] = None # A value indicating the "base" the of the item. This enum is a useful but dramatic oversimplification of what it means for an item to have a "Type". Still, it's handy in many situations. itemCategoryHashes are the preferred way of identifying types, we have retained this enum for its convenience.
item_type_and_tier_display_name: t.Optional[
str
] = None # It became a common enough pattern in our UI to show Item Type and Tier combined into a single localized string that I'm just going to go ahead and start pre-creating these for items.
item_type_display_name: t.Optional[
str
] = None # The localized title/name of the item's type. This can be whatever the designers want, and has no guarantee of consistency between items.
links: t.Optional[
t.Sequence["HyperlinkReference"]
] = None # If we added any help or informational URLs about this item, these will be those links.
lore_hash: t.Optional[
int
] = None # If the item has any related Lore (DestinyLoreDefinition), this will be the hash identifier you can use to look up the lore definition.
metrics: t.Optional[
"DestinyItemMetricBlockDefinition"
] = None # If this item has available metrics to be shown, this block will be non-null have the appropriate hashes defined.
non_transferrable: t.Optional[
bool
] = None # The intrinsic transferability of an item. I hate that this boolean is negative - but there's a reason. Just because an item is intrinsically transferrable doesn't mean that it can be transferred, and we don't want to imply that this is the only source of that transferability.
objectives: t.Optional[
"DestinyItemObjectiveBlockDefinition"
] = None # If this item has Objectives (extra tasks that can be accomplished related to the item... most frequently when the item is a Quest Step and the Objectives need to be completed to move on to the next Quest Step), this block will be non-null and the objectives defined herein.
perks: t.Optional[
t.Sequence["DestinyItemPerkEntryDefinition"]
] = None # If the item has any *intrinsic* Perks (Perks that it will provide regardless of Sockets, Talent Grid, and other transitory state), they will be defined here.
plug: t.Optional[
"DestinyItemPlugDefinition"
] = None # If this item *is* a Plug, this will be non-null and the info defined herein. See DestinyItemPlugDefinition for more information.
preview: t.Optional[
"DestinyItemPreviewBlockDefinition"
] = None # If this item can be Used or Acquired to gain other items (for instance, how Eververse Boxes can be consumed to get items from the box), this block will be non-null and will give summary information for the items that can be acquired.
quality: t.Optional[
"DestinyItemQualityBlockDefinition"
] = None # If this item can have a level or stats, this block will be non-null and will be populated with default quality (item level, "quality", and infusion) data. See the block for more details, there's often less upfront information in D2 so you'll want to be aware of how you use quality and item level on the definition level now.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
sack: t.Optional[
"DestinyItemSackBlockDefinition"
] = None # If this item is a "reward sack" that can be opened to provide other items, this will be non-null and the properties of the sack contained herein.
screenshot: t.Optional[
str
] = None # If we were able to acquire an in-game screenshot for the item, the path to that screenshot will be returned here. Note that not all items have screenshots: particularly not any non-equippable items.
season_hash: t.Optional[
int
] = None # If this item is related directly to a Season of Destiny, this is the hash identifier for that season.
secondary_icon: t.Optional[
str
] = None # A secondary icon associated with the item. Currently this is used in very context specific applications, such as Emblem Nameplates.
secondary_overlay: t.Optional[
str
] = None # Pulled from the secondary icon, this is the "secondary background" of the secondary icon. Confusing? Sure, that's why I call it "overlay" here: because as far as it's been used thus far, it has been for an optional overlay image. We'll see if that holds up, but at least for now it explains what this image is a bit better.
secondary_special: t.Optional[
str
] = None # Pulled from the Secondary Icon, this is the "special" background for the item. For Emblems, this is the background image used on the Details view: but it need not be limited to that for other types of items.
set_data: t.Optional[
"DestinyItemSetBlockDefinition"
] = None # If this item is a quest, this block will be non-null. In practice, I wish I had called this the Quest block, but at the time it wasn't clear to me whether it would end up being used for purposes other than quests. It will contain data about the steps in the quest, and mechanics we can use for displaying and tracking the quest.
sockets: t.Optional[
"DestinyItemSocketBlockDefinition"
] = None # If this item has any Sockets, this will be non-null and the individual sockets on the item will be defined herein.
source_data: t.Optional[
"DestinyItemSourceBlockDefinition"
] = None # If this item has a known source, this block will be non-null and populated with source information. Unfortunately, at this time we are not generating sources: that is some aggressively manual work which we didn't have time for, and I'm hoping to get back to at some point in the future.
special_item_type: t.Optional[
"SpecialItemType"
] = None # In Destiny 1, we identified some items as having particular categories that we'd like to know about for various internal logic purposes. These are defined in SpecialItemType, and while these days the itemCategoryHashes are the preferred way of identifying types, we have retained this enum for its convenience.
stats: t.Optional[
"DestinyItemStatBlockDefinition"
] = None # If this item can have stats (such as a weapon, armor, or vehicle), this block will be non-null and populated with the stats found on the item.
summary: t.Optional[
"DestinyItemSummaryBlockDefinition"
] = None # Summary data about the item.
summary_item_hash: t.Optional[
int
] = None # There are times when the game will show you a "summary/vague" version of an item - such as a description of its type represented as a DestinyInventoryItemDefinition - rather than display the item itself. This happens sometimes when summarizing possible rewards in a tooltip. This is the item displayed instead, if it exists.
talent_grid: t.Optional[
"DestinyItemTalentGridBlockDefinition"
] = None # If the item has a Talent Grid, this will be non-null and the properties of the grid defined herein. Note that, while many items still have talent grids, the only ones with meaningful Nodes still on them will be Subclass/"Build" items.
tooltip_notifications: t.Optional[
t.Sequence["DestinyItemTooltipNotification"]
] = None # Tooltips that only come up conditionally for the item. Check the live data DestinyItemComponent.tooltipNotificationIndexes property for which of these should be shown at runtime.
tooltip_style: t.Optional[
str
] = None # An identifier that the game UI uses to determine what type of tooltip to show for the item. These have no corresponding definitions that BNet can link to: so it'll be up to you to interpret and display your UI differently according to these styles (or ignore it).
trait_hashes: t.Optional[
t.Sequence[int]
] = None # These are the corresponding trait definition hashes for the entries in traitIds.
trait_ids: t.Optional[
t.Sequence[str]
] = None # Traits are metadata tags applied to this item. For example: armor slot, weapon type, foundry, faction, etc. These IDs come from the game and don't map to any content, but should still be useful.
translation_block: t.Optional[
"DestinyItemTranslationBlockDefinition"
] = None # If this item can be rendered, this block will be non-null and will be populated with rendering information.
ui_item_display_style: t.Optional[
str
] = None # A string identifier that the game's UI uses to determine how the item should be rendered in inventory screens and the like. This could really be anything - at the moment, we don't have the time to really breakdown and maintain all the possible strings this could be, partly because new ones could be added ad hoc. But if you want to use it to dictate your own UI, or look for items with a certain display style, go for it!
value: t.Optional[
"DestinyItemValueBlockDefinition"
] = None # The conceptual "Value" of an item, if any was defined. See the DestinyItemValueBlockDefinition for more details.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"tooltipNotifications": to_json(self.tooltip_notifications),
"collectibleHash": to_json(self.collectible_hash),
"iconWatermark": to_json(self.icon_watermark),
"iconWatermarkShelved": to_json(self.icon_watermark_shelved),
"secondaryIcon": to_json(self.secondary_icon),
"secondaryOverlay": to_json(self.secondary_overlay),
"secondarySpecial": to_json(self.secondary_special),
"backgroundColor": to_json(self.background_color),
"screenshot": to_json(self.screenshot),
"itemTypeDisplayName": to_json(self.item_type_display_name),
"flavorText": to_json(self.flavor_text),
"uiItemDisplayStyle": to_json(self.ui_item_display_style),
"itemTypeAndTierDisplayName": to_json(self.item_type_and_tier_display_name),
"displaySource": to_json(self.display_source),
"tooltipStyle": to_json(self.tooltip_style),
"action": to_json(self.action),
"inventory": to_json(self.inventory),
"setData": to_json(self.set_data),
"stats": to_json(self.stats),
"emblemObjectiveHash": to_json(self.emblem_objective_hash),
"equippingBlock": to_json(self.equipping_block),
"translationBlock": to_json(self.translation_block),
"preview": to_json(self.preview),
"quality": to_json(self.quality),
"value": to_json(self.value),
"sourceData": to_json(self.source_data),
"objectives": to_json(self.objectives),
"metrics": to_json(self.metrics),
"plug": to_json(self.plug),
"gearset": to_json(self.gearset),
"sack": to_json(self.sack),
"sockets": to_json(self.sockets),
"summary": to_json(self.summary),
"talentGrid": to_json(self.talent_grid),
"investmentStats": to_json(self.investment_stats),
"perks": to_json(self.perks),
"loreHash": to_json(self.lore_hash),
"summaryItemHash": to_json(self.summary_item_hash),
"animations": to_json(self.animations),
"allowActions": to_json(self.allow_actions),
"links": to_json(self.links),
"doesPostmasterPullHaveSideEffects": to_json(
self.does_postmaster_pull_have_side_effects
),
"nonTransferrable": to_json(self.non_transferrable),
"itemCategoryHashes": to_json(self.item_category_hashes),
"specialItemType": to_json(self.special_item_type),
"itemType": to_json(self.item_type),
"itemSubType": to_json(self.item_sub_type),
"classType": to_json(self.class_type),
"breakerType": to_json(self.breaker_type),
"breakerTypeHash": to_json(self.breaker_type_hash),
"equippable": to_json(self.equippable),
"damageTypeHashes": to_json(self.damage_type_hashes),
"damageTypes": to_json(self.damage_types),
"defaultDamageType": to_json(self.default_damage_type),
"defaultDamageTypeHash": to_json(self.default_damage_type_hash),
"seasonHash": to_json(self.season_hash),
"isWrapper": to_json(self.is_wrapper),
"traitIds": to_json(self.trait_ids),
"traitHashes": to_json(self.trait_hashes),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemActionBlockDefinition:
"""If an item can have an action performed on it (like "Dismantle"), it
will be defined here if you care."""
action_type_label: t.Optional[str] = None # The internal identifier for the action.
consume_entire_stack: t.Optional[
bool
] = None # If true, the entire stack is deleted when the action completes.
delete_on_action: t.Optional[
bool
] = None # If true, the item is deleted when the action completes.
is_positive: t.Optional[
bool
] = None # The content has this property, however it's not entirely clear how it is used.
overlay_icon: t.Optional[
str
] = None # The icon associated with the overlay screen for the action, if any.
overlay_screen_name: t.Optional[
str
] = None # If the action has an overlay screen associated with it, this is the name of that screen. Unfortunately, we cannot return the screen's data itself.
progression_rewards: t.Optional[
t.Sequence["DestinyProgressionRewardDefinition"]
] = None # If performing this action earns you Progression, this is the list of progressions and values granted for those progressions by performing this action.
required_cooldown_hash: t.Optional[
int
] = None # The identifier hash for the Cooldown associated with this action. We have not pulled this data yet for you to have more data to use for cooldowns.
required_cooldown_seconds: t.Optional[
int
] = None # The number of seconds to delay before allowing this action to be performed again.
required_items: t.Optional[
t.Sequence["DestinyItemActionRequiredItemDefinition"]
] = None # If the action requires other items to exist or be destroyed, this is the list of those items and requirements.
required_location: t.Optional[
str
] = None # Theoretically, an item could have a localized string for a hint about the location in which the action should be performed. In practice, no items yet have this property.
use_on_acquire: t.Optional[
bool
] = None # If true, this action will be performed as soon as you earn this item. Some rewards work this way, providing you a single item to pick up from a reward-granting vendor in-game and then immediately consuming itself to provide you multiple items.
verb_description: t.Optional[
str
] = None # Localized text describing the action being performed.
verb_name: t.Optional[
str
] = None # Localized text for the verb of the action being performed.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"verbName": to_json(self.verb_name),
"verbDescription": to_json(self.verb_description),
"isPositive": to_json(self.is_positive),
"overlayScreenName": to_json(self.overlay_screen_name),
"overlayIcon": to_json(self.overlay_icon),
"requiredCooldownSeconds": to_json(self.required_cooldown_seconds),
"requiredItems": to_json(self.required_items),
"progressionRewards": to_json(self.progression_rewards),
"actionTypeLabel": to_json(self.action_type_label),
"requiredLocation": to_json(self.required_location),
"requiredCooldownHash": to_json(self.required_cooldown_hash),
"deleteOnAction": to_json(self.delete_on_action),
"consumeEntireStack": to_json(self.consume_entire_stack),
"useOnAcquire": to_json(self.use_on_acquire),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemActionRequiredItemDefinition:
"""The definition of an item and quantity required in a character's
inventory in order to perform an action."""
count: t.Optional[int] = None # The minimum quantity of the item you have to have.
delete_on_action: t.Optional[
bool
] = None # If true, the item/quantity will be deleted from your inventory when the action is performed. Otherwise, you'll retain these required items after the action is complete.
item_hash: t.Optional[
int
] = None # The hash identifier of the item you need to have. Use it to look up the DestinyInventoryItemDefinition for more info.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"count": to_json(self.count),
"itemHash": to_json(self.item_hash),
"deleteOnAction": to_json(self.delete_on_action),
}
[docs]@dt.dataclass(frozen=True)
class DestinyProgressionRewardDefinition:
"""Inventory Items can reward progression when actions are performed on
them.
A common example of this in Destiny 1 was Bounties, which would
reward Experience on your Character and the like when you completed
the bounty. Note that this maps to a
DestinyProgressionMappingDefinition, and *not* a
DestinyProgressionDefinition directly. This is apparently so that
multiple progressions can be granted progression points/experience
at the same time.
"""
amount: t.Optional[
int
] = None # The amount of experience to give to each of the mapped progressions.
apply_throttles: t.Optional[
bool
] = None # If true, the game's internal mechanisms to throttle progression should be applied.
progression_mapping_hash: t.Optional[
int
] = None # The hash identifier of the DestinyProgressionMappingDefinition that contains the progressions for which experience should be applied.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"progressionMappingHash": to_json(self.progression_mapping_hash),
"amount": to_json(self.amount),
"applyThrottles": to_json(self.apply_throttles),
}
[docs]@dt.dataclass(frozen=True)
class DestinyProgressionMappingDefinition:
"""Aggregations of multiple progressions.
These are used to apply rewards to multiple progressions at once.
They can sometimes have human readable data as well, but only
extremely sporadically.
"""
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # Infrequently defined in practice. Defer to the individual progressions' display properties.
display_units: t.Optional[
str
] = None # The localized unit of measurement for progression across the progressions defined in this mapping. Unfortunately, this is very infrequently defined. Defer to the individual progressions' display units.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"displayUnits": to_json(self.display_units),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemInventoryBlockDefinition:
"""If the item can exist in an inventory - the overwhelming majority of them can and do - then this is the basic properties regarding the item's relationship with the inventory."""
bucket_type_hash: t.Optional[
int
] = None # The hash identifier for the DestinyInventoryBucketDefinition to which this item belongs. I should have named this "bucketHash", but too many things refer to it now. Sigh.
expiration_tooltip: t.Optional[
str
] = None # The tooltip message to show, if any, when the item expires.
expired_in_activity_message: t.Optional[
str
] = None # If the item expires while playing in an activity, we show a different message.
expired_in_orbit_message: t.Optional[
str
] = None # If the item expires in orbit, we show a... more different message. ("Consummate V's, consummate!")
is_instance_item: t.Optional[
bool
] = None # If TRUE, this item is instanced. Otherwise, it is a generic item that merely has a quantity in a stack (like Glimmer).
max_stack_size: t.Optional[
int
] = None # The maximum quantity of this item that can exist in a stack.
recovery_bucket_type_hash: t.Optional[
int
] = None # If the item is picked up by the lost loot queue, this is the hash identifier for the DestinyInventoryBucketDefinition into which it will be placed. Again, I should have named this recoveryBucketHash instead.
stack_unique_label: t.Optional[
str
] = None # If this string is populated, you can't have more than one stack with this label in a given inventory. Note that this is different from the equipping block's unique label, which is used for equipping uniqueness.
suppress_expiration_when_objectives_complete: t.Optional[bool] = None
tier_type: t.Optional[
"TierType"
] = None # The enumeration matching the tier type of the item to known values, again for convenience sake.
tier_type_hash: t.Optional[
int
] = None # The hash identifier for the Tier Type of the item, use to look up its DestinyItemTierTypeDefinition if you need to show localized data for the item's tier.
tier_type_name: t.Optional[
str
] = None # The localized name of the tier type, which is a useful shortcut so you don't have to look up the definition every time. However, it's mostly a holdover from days before we had a DestinyItemTierTypeDefinition to refer to.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"stackUniqueLabel": to_json(self.stack_unique_label),
"maxStackSize": to_json(self.max_stack_size),
"bucketTypeHash": to_json(self.bucket_type_hash),
"recoveryBucketTypeHash": to_json(self.recovery_bucket_type_hash),
"tierTypeHash": to_json(self.tier_type_hash),
"isInstanceItem": to_json(self.is_instance_item),
"tierTypeName": to_json(self.tier_type_name),
"tierType": to_json(self.tier_type),
"expirationTooltip": to_json(self.expiration_tooltip),
"expiredInActivityMessage": to_json(self.expired_in_activity_message),
"expiredInOrbitMessage": to_json(self.expired_in_orbit_message),
"suppressExpirationWhenObjectivesComplete": to_json(
self.suppress_expiration_when_objectives_complete
),
}
[docs]@dt.dataclass(frozen=True)
class DestinyInventoryBucketDefinition:
"""An Inventory (be it Character or Profile level) is comprised of many
Buckets.
An example of a bucket is "Primary Weapons", where all of the primary weapons on a character are gathered together into a single visual element in the UI: a subset of the inventory that has a limited number of slots, and in this case also has an associated Equipment Slot for equipping an item in the bucket.
Item definitions declare what their "default" bucket is (DestinyInventoryItemDefinition.inventory.bucketTypeHash), and Item instances will tell you which bucket they are currently residing in (DestinyItemComponent.bucketHash). You can use this information along with the DestinyInventoryBucketDefinition to show these items grouped by bucket.
You cannot transfer an item to a bucket that is not its Default without going through a Vendor's "accepted items" (DestinyVendorDefinition.acceptedItems). This is how transfer functionality like the Vault is implemented, as a feature of a Vendor. See the vendor's acceptedItems property for more details.
"""
bucket_order: t.Optional[
int
] = None # Use this property to provide a quick-and-dirty recommended ordering for buckets in the UI. Most UIs will likely want to forsake this for something more custom and manual.
category: t.Optional[
"BucketCategory"
] = None # An enum value for what items can be found in the bucket. See the BucketCategory enum for more details.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
enabled: t.Optional[
bool
] = None # If True, this bucket is enabled. Disabled buckets may include buckets that were included for test purposes, or that were going to be used but then were abandoned but never removed from content *cough*.
fifo: t.Optional[
bool
] = None # if a FIFO bucket fills up, it will delete the oldest item from said bucket when a new item tries to be added to it. If this is FALSE, the bucket will not allow new items to be placed in it until room is made by the user manually deleting items from it. You can see an example of this with the Postmaster's bucket.
has_transfer_destination: t.Optional[
bool
] = None # If TRUE, there is at least one Vendor that can transfer items to/from this bucket. See the DestinyVendorDefinition's acceptedItems property for more information on how transferring works.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
item_count: t.Optional[
int
] = None # The maximum # of item "slots" in a bucket. A slot is a given combination of item + quantity. For instance, a Weapon will always take up a single slot, and always have a quantity of 1. But a material could take up only a single slot with hundreds of quantity.
location: t.Optional[
"ItemLocation"
] = None # Sometimes, inventory buckets represent conceptual "locations" in the game that might not be expected. This value indicates the conceptual location of the bucket, regardless of where it is actually contained on the character/account. See ItemLocation for details. Note that location includes the Vault and the Postmaster (both of whom being just inventory buckets with additional actions that can be performed on them through a Vendor)
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
scope: t.Optional[
"BucketScope"
] = None # Where the bucket is found. 0 = Character, 1 = Account
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"scope": to_json(self.scope),
"category": to_json(self.category),
"bucketOrder": to_json(self.bucket_order),
"itemCount": to_json(self.item_count),
"location": to_json(self.location),
"hasTransferDestination": to_json(self.has_transfer_destination),
"enabled": to_json(self.enabled),
"fifo": to_json(self.fifo),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSetBlockDefinition:
"""Primarily for Quests, this is the definition of properties related to
the item if it is a quest and its various quest steps."""
item_list: t.Optional[
t.Sequence["DestinyItemSetBlockEntryDefinition"]
] = None # A collection of hashes of set items, for items such as Quest Metadata items that possess this data.
quest_line_description: t.Optional[
str
] = None # The description of the quest line that this quest step is a part of.
quest_line_name: t.Optional[
str
] = None # The name of the quest line that this quest step is a part of.
quest_step_summary: t.Optional[
str
] = None # An additional summary of this step in the quest line.
require_ordered_set_item_add: t.Optional[
bool
] = None # If true, items in the set can only be added in increasing order, and adding an item will remove any previous item. For Quests, this is by necessity true. Only one quest step is present at a time, and previous steps are removed as you advance in the quest.
set_is_featured: t.Optional[
bool
] = None # If true, the UI should treat this quest as "featured"
set_type: t.Optional[
str
] = None # A string identifier we can use to attempt to identify the category of the Quest.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"itemList": to_json(self.item_list),
"requireOrderedSetItemAdd": to_json(self.require_ordered_set_item_add),
"setIsFeatured": to_json(self.set_is_featured),
"setType": to_json(self.set_type),
"questLineName": to_json(self.quest_line_name),
"questLineDescription": to_json(self.quest_line_description),
"questStepSummary": to_json(self.quest_step_summary),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSetBlockEntryDefinition:
"""Defines a particular entry in an ItemSet (AKA a particular Quest Step in
a Quest)"""
item_hash: t.Optional[
int
] = None # This is the hash identifier for a DestinyInventoryItemDefinition representing this quest step.
tracking_value: t.Optional[
int
] = None # Used for tracking which step a user reached. These values will be populated in the user's internal state, which we expose externally as a more usable DestinyQuestStatus object. If this item has been obtained, this value will be set in trackingUnlockValueHash.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"trackingValue": to_json(self.tracking_value),
"itemHash": to_json(self.item_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemStatBlockDefinition:
"""Information about the item's calculated stats, with as much data as we
can find for the stats without having an actual instance of the item.
Note that this means the entire concept of providing these stats is
fundamentally insufficient: we cannot predict with 100% accuracy the
conditions under which an item can spawn, so we use various
heuristics to attempt to simulate the conditions as accurately as
possible. Actual stats for items in-game can and will vary, but
these should at least be useful base points for comparison and
display. It is also worth noting that some stats, like Magazine
size, have further calculations performed on them by scripts in-game
and on the game servers that BNet does not have access to. We cannot
know how those stats are further transformed, and thus some stats
will be inaccurate even on instances of items in BNet vs. how they
appear in-game. This is a known limitation of our item statistics,
without any planned fix.
"""
disable_primary_stat_display: t.Optional[
bool
] = None # If true, the game won't show the "primary" stat on this item when you inspect it. NOTE: This is being manually mapped, because I happen to want it in a block that isn't going to directly create this derivative block.
has_displayable_stats: t.Optional[
bool
] = None # A quick and lazy way to determine whether any stat other than the "primary" stat is actually visible on the item. Items often have stats that we return in case people find them useful, but they're not part of the "Stat Group" and thus we wouldn't display them in our UI. If this is False, then we're not going to display any of these stats other than the primary one.
primary_base_stat_hash: t.Optional[
int
] = None # This stat is determined to be the "primary" stat, and can be looked up in the stats or any other stat collection related to the item. Use this hash to look up the stat's value using DestinyInventoryItemDefinition.stats.stats, and the renderable data for the primary stat in the related DestinyStatDefinition.
stat_group_hash: t.Optional[
int
] = None # If the item's stats are meant to be modified by a DestinyStatGroupDefinition, this will be the identifier for that definition. If you are using live data or precomputed stats data on the DestinyInventoryItemDefinition.stats.stats property, you don't have to worry about statGroupHash and how it alters stats: the already altered stats are provided to you. But if you want to see how the sausage gets made, or perform computations yourself, this is valuable information.
stats: t.Optional[
t.Mapping[str, "DestinyInventoryItemStatDefinition"]
] = None # If you are looking for precomputed values for the stats on a weapon, this is where they are stored. Technically these are the "Display" stat values. Please see DestinyStatsDefinition for what Display Stat Values means, it's a very long story... but essentially these are the closest values BNet can get to the item stats that you see in-game. These stats are keyed by the DestinyStatDefinition's hash identifier for the stat that's found on the item.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"disablePrimaryStatDisplay": to_json(self.disable_primary_stat_display),
"statGroupHash": to_json(self.stat_group_hash),
"stats": to_json(self.stats),
"hasDisplayableStats": to_json(self.has_displayable_stats),
"primaryBaseStatHash": to_json(self.primary_base_stat_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyInventoryItemStatDefinition:
"""Defines a specific stat value on an item, and the minimum/maximum range
that we could compute for the item based on our heuristics for how the item
might be generated.
Not guaranteed to match real-world instances of the item, but should
hopefully at least be close. If it's not close, let us know on the
Bungie API forums.
"""
display_maximum: t.Optional[
int
] = None # The maximum possible value for the stat as shown in the UI, if it is being shown somewhere that reveals maximum in the UI (such as a bar chart-style view). This is pulled directly from the item's DestinyStatGroupDefinition, and placed here for convenience. If not returned, there is no maximum to use (and thus the stat should not be shown in a way that assumes there is a limit to the stat)
maximum: t.Optional[
int
] = None # The maximum possible value for this stat that we think the item can roll. WARNING: In Destiny 1, this field was calculated using the potential stat rolls on the item's talent grid. In Destiny 2, items no longer have meaningful talent grids and instead have sockets: but the calculation of this field was never altered to adapt to this change. As such, this field should be considered deprecated until we can address this oversight.
minimum: t.Optional[
int
] = None # The minimum possible value for this stat that we think the item can roll.
stat_hash: t.Optional[
int
] = None # The hash for the DestinyStatDefinition representing this stat.
value: t.Optional[
int
] = None # This value represents the stat value assuming the minimum possible roll but accounting for any mandatory bonuses that should be applied to the stat on item creation. In Destiny 1, this was different from the "minimum" value because there were certain conditions where an item could be theoretically lower level/value than the initial roll. In Destiny 2, this is not possible unless Talent Grids begin to be used again for these purposes or some other system change occurs... thus in practice, value and minimum should be the same in Destiny 2. Good riddance.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"statHash": to_json(self.stat_hash),
"value": to_json(self.value),
"minimum": to_json(self.minimum),
"maximum": to_json(self.maximum),
"displayMaximum": to_json(self.display_maximum),
}
[docs]@dt.dataclass(frozen=True)
class DestinyStatDefinition:
"""This represents a stat that's applied to a character or an item (such as
a weapon, piece of armor, or a vehicle). An example of a stat might be
Attack Power on a weapon. Stats go through a complex set of transformations
before they end up being shown to the user as a number or a progress bar,
and those transformations are fundamentally intertwined with the concept of
a "Stat Group" (DestinyStatGroupDefinition). Items have both Stats and a
reference to a Stat Group, and it is the Stat Group that takes the raw stat
information and gives it both rendering metadata (such as whether to show
it as a number or a progress bar) and the final transformation data
(interpolation tables to turn the raw investment stat into a display stat).
Please see DestinyStatGroupDefinition for more information on that
transformational process. Stats are segregated from Stat Groups because
different items and types of items can refer to the same stat, but have
different "scales" for the stat while still having the same underlying
value. For example, both a Shotgun and an Auto Rifle may have a "raw"
impact stat of 50, but the Auto Rifle's Stat Group will scale that 50 down
so that, when it is displayed, it is a smaller value relative to the
shotgun. (this is a totally made up example, don't assume shotguns have
naturally higher impact than auto rifles because of this)
A final caveat is that some stats, even after this "final" transformation, go through yet another set of transformations directly in the game as a result of dynamic, stateful scripts that get run. BNet has no access to these scripts, nor any way to know which scripts get executed. As a result, the stats for an item that you see in-game - particularly for stats that are often impacted by Perks, like Magazine Size - can change dramatically from what we return on Bungie.Net. This is a known issue with no fix coming down the pipeline. Take these stats with a grain of salt.
Stats actually go through four transformations, for those interested:
1) "Sandbox" stat, the "most raw" form. These are pretty much useless without transformations applied, and thus are not currently returned in the API. If you really want these, we can provide them. Maybe someone could do something cool with it?
2) "Investment" stat (the stat's value after DestinyStatDefinition's interpolation tables and aggregation logic is applied to the "Sandbox" stat value)
3) "Display" stat (the stat's base UI-visible value after DestinyStatGroupDefinition's interpolation tables are applied to the Investment Stat value. For most stats, this is what is displayed.)
4) Underlying in-game stat (the stat's actual value according to the game, after the game runs dynamic scripts based on the game and character's state. This is the final transformation that BNet does not have access to. For most stats, this is not actually displayed to the user, with the exception of Magazine Size which is then piped back to the UI for display in-game, but not to BNet.)
"""
aggregation_type: t.Optional[
"DestinyStatAggregationType"
] = None # Stats can exist on a character or an item, and they may potentially be aggregated in different ways. The DestinyStatAggregationType enum value indicates the way that this stat is being aggregated.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
has_computed_block: t.Optional[
bool
] = None # True if the stat is computed rather than being delivered as a raw value on items. For instance, the Light stat in Destiny 1 was a computed stat.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
stat_category: t.Optional[
"DestinyStatCategory"
] = None # The category of the stat, according to the game.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"aggregationType": to_json(self.aggregation_type),
"hasComputedBlock": to_json(self.has_computed_block),
"statCategory": to_json(self.stat_category),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyStatGroupDefinition:
"""When an inventory item (DestinyInventoryItemDefinition) has Stats (such
as Attack Power), the item will refer to a Stat Group.
This definition enumerates the properties used to transform the
item's "Investment" stats into "Display" stats. See
DestinyStatDefinition's documentation for information about the
transformation of Stats, and the meaning of an Investment vs. a
Display stat. If you don't want to do these calculations on your
own, fear not: pulling live data from the BNet endpoints will return
display stat values pre-computed and ready for you to use. I highly
recommend this approach, saves a lot of time and also accounts for
certain stat modifiers that can't easily be accounted for without
live data (such as stat modifiers on Talent Grids and Socket Plugs)
"""
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
maximum_value: t.Optional[
int
] = None # The maximum possible value that any stat in this group can be transformed into. This is used by stats that *don't* have scaledStats entries below, but that still need to be displayed as a progress bar, in which case this is used as the upper bound for said progress bar. (the lower bound is always 0)
overrides: t.Optional[
t.Mapping[str, "DestinyStatOverrideDefinition"]
] = None # The game has the ability to override, based on the stat group, what the localized text is that is displayed for Stats being shown on the item. Mercifully, no Stat Groups use this feature currently. If they start using them, we'll all need to start using them (and those of you who are more prudent than I am can go ahead and start pre-checking for this.)
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
scaled_stats: t.Optional[
t.Sequence["DestinyStatDisplayDefinition"]
] = None # Any stat that requires scaling to be transformed from an "Investment" stat to a "Display" stat will have an entry in this list. For more information on what those types of stats mean and the transformation process, see DestinyStatDefinition. In retrospect, I wouldn't mind if this was a dictionary keyed by the stat hash instead. But I'm going to leave it be because [[After Apple Picking]].
ui_position: t.Optional[
int
] = None # This apparently indicates the position of the stats in the UI? I've returned it in case anyone can use it, but it's not of any use to us on BNet. Something's being lost in translation with this value.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"maximumValue": to_json(self.maximum_value),
"uiPosition": to_json(self.ui_position),
"scaledStats": to_json(self.scaled_stats),
"overrides": to_json(self.overrides),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyStatDisplayDefinition:
"""Describes the way that an Item Stat (see DestinyStatDefinition) is
transformed using the DestinyStatGroupDefinition related to that item.
See both of the aforementioned definitions for more information
about the stages of stat transformation. This represents the
transformation of a stat into a "Display" stat (the closest value
that BNet can get to the in-game display value of the stat)
"""
display_as_numeric: t.Optional[
bool
] = None # If this is true, the stat should be displayed as a number. Otherwise, display it as a progress bar. Or, you know, do whatever you want. There's no displayAsNumeric police.
display_interpolation: t.Optional[
t.Sequence["InterpolationPoint"]
] = None # The interpolation table representing how the Investment Stat is transformed into a Display Stat. See DestinyStatDefinition for a description of the stages of stat transformation.
maximum_value: t.Optional[
int
] = None # Regardless of the output of interpolation, this is the maximum possible value that the stat can be. It should also be used as the upper bound for displaying the stat as a progress bar (the minimum always being 0)
stat_hash: t.Optional[
int
] = None # The hash identifier for the stat being transformed into a Display stat. Use it to look up the DestinyStatDefinition, or key into a DestinyInventoryItemDefinition's stats property.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"statHash": to_json(self.stat_hash),
"maximumValue": to_json(self.maximum_value),
"displayAsNumeric": to_json(self.display_as_numeric),
"displayInterpolation": to_json(self.display_interpolation),
}
[docs]@dt.dataclass(frozen=True)
class DestinyStatOverrideDefinition:
"""Stat Groups (DestinyStatGroupDefinition) has the ability to override the
localized text associated with stats that are to be shown on the items with
which they are associated.
This defines a specific overridden stat. You could theoretically
check these before rendering your stat UI, and for each stat that
has an override show these displayProperties instead of those on the
DestinyStatDefinition. Or you could be like us, and skip that for
now because the game has yet to actually use this feature. But know
that it's here, waiting for a resilliant young designer to take up
the mantle and make us all look foolish by showing the wrong name
for stats. Note that, if this gets used, the override will apply
only to items using the overriding Stat Group. Other items will
still show the default stat's name/description.
"""
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The display properties to show instead of the base DestinyStatDefinition display properties.
stat_hash: t.Optional[
int
] = None # The hash identifier of the stat whose display properties are being overridden.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"statHash": to_json(self.stat_hash),
"displayProperties": to_json(self.display_properties),
}
[docs]@dt.dataclass(frozen=True)
class DestinyEquippingBlockDefinition:
"""Items that can be equipped define this block.
It contains information we need to understand how and when the item
can be equipped.
"""
ammo_type: t.Optional[
"DestinyAmmunitionType"
] = None # Ammo type used by a weapon is no longer determined by the bucket in which it is contained. If the item has an ammo type - i.e. if it is a weapon - this will be the type of ammunition expected.
attributes: t.Optional[
"EquippingItemBlockAttributes"
] = None # These are custom attributes on the equippability of the item. For now, this can only be "equip on acquire", which would mean that the item will be automatically equipped as soon as you pick it up.
display_strings: t.Optional[
t.Sequence[str]
] = None # These are strings that represent the possible Game/Account/Character state failure conditions that can occur when trying to equip the item. They match up one-to-one with requiredUnlockExpressions.
equipment_slot_type_hash: t.Optional[
int
] = None # An equipped item *must* be equipped in an Equipment Slot. This is the hash identifier of the DestinyEquipmentSlotDefinition into which it must be equipped.
gearset_item_hash: t.Optional[
int
] = None # If the item is part of a gearset, this is a reference to that gearset item.
unique_label: t.Optional[
str
] = None # If defined, this is the label used to check if the item has other items of matching types already equipped. For instance, when you aren't allowed to equip more than one Exotic Weapon, that's because all exotic weapons have identical uniqueLabels and the game checks the to-be-equipped item's uniqueLabel vs. all other already equipped items (other than the item in the slot that's about to be occupied).
unique_label_hash: t.Optional[
int
] = None # The hash of that unique label. Does not point to a specific definition.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"gearsetItemHash": to_json(self.gearset_item_hash),
"uniqueLabel": to_json(self.unique_label),
"uniqueLabelHash": to_json(self.unique_label_hash),
"equipmentSlotTypeHash": to_json(self.equipment_slot_type_hash),
"attributes": to_json(self.attributes),
"ammoType": to_json(self.ammo_type),
"displayStrings": to_json(self.display_strings),
}
[docs]@dt.dataclass(frozen=True)
class DestinyEquipmentSlotDefinition:
"""Characters can not only have Inventory buckets (containers of items that
are generally matched by their type or functionality), they can also have
Equipment Slots.
The Equipment Slot is an indicator that the related bucket can have
instanced items equipped on the character. For instance, the Primary
Weapon bucket has an Equipment Slot that determines whether you can
equip primary weapons, and holds the association between its slot
and the inventory bucket from which it can have items equipped. An
Equipment Slot must have a related Inventory Bucket, but not all
inventory buckets must have Equipment Slots.
"""
apply_custom_art_dyes: t.Optional[
bool
] = None # If True, equipped items should have their custom art dyes applied when rendering the item. Otherwise, custom art dyes on an item should be ignored if the item is equipped in this slot.
art_dye_channels: t.Optional[
t.Sequence["DestinyArtDyeReference"]
] = None # The Art Dye Channels that apply to this equipment slot.
bucket_type_hash: t.Optional[
int
] = None # The inventory bucket that owns this equipment slot.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
equipment_category_hash: t.Optional[
int
] = None # These technically point to "Equipment Category Definitions". But don't get excited. There's nothing of significant value in those definitions, so I didn't bother to expose them. You can use the hash here to group equipment slots by common functionality, which serves the same purpose as if we had the Equipment Category definitions exposed.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"equipmentCategoryHash": to_json(self.equipment_category_hash),
"bucketTypeHash": to_json(self.bucket_type_hash),
"applyCustomArtDyes": to_json(self.apply_custom_art_dyes),
"artDyeChannels": to_json(self.art_dye_channels),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyArtDyeReference:
art_dye_channel_hash: t.Optional[int] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"artDyeChannelHash": to_json(self.art_dye_channel_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemTranslationBlockDefinition:
"""This Block defines the rendering data associated with the item, if
any."""
arrangements: t.Optional[t.Sequence["DestinyGearArtArrangementReference"]] = None
custom_dyes: t.Optional[t.Sequence["DyeReference"]] = None
default_dyes: t.Optional[t.Sequence["DyeReference"]] = None
has_geometry: t.Optional[bool] = None
locked_dyes: t.Optional[t.Sequence["DyeReference"]] = None
weapon_pattern_hash: t.Optional[int] = None
weapon_pattern_identifier: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"weaponPatternIdentifier": to_json(self.weapon_pattern_identifier),
"weaponPatternHash": to_json(self.weapon_pattern_hash),
"defaultDyes": to_json(self.default_dyes),
"lockedDyes": to_json(self.locked_dyes),
"customDyes": to_json(self.custom_dyes),
"arrangements": to_json(self.arrangements),
"hasGeometry": to_json(self.has_geometry),
}
[docs]@dt.dataclass(frozen=True)
class DestinyGearArtArrangementReference:
art_arrangement_hash: t.Optional[int] = None
class_hash: t.Optional[int] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"classHash": to_json(self.class_hash),
"artArrangementHash": to_json(self.art_arrangement_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemPreviewBlockDefinition:
"""Items like Sacks or Boxes can have items that it shows in-game when you
view details that represent the items you can obtain if you use or acquire
the item.
This defines those categories, and gives some insights into that
data's source.
"""
artifact_hash: t.Optional[
int
] = None # If this item should show you Artifact information when you preview it, this is the hash identifier of the DestinyArtifactDefinition for the artifact whose data should be shown.
derived_item_categories: t.Optional[
t.Sequence["DestinyDerivedItemCategoryDefinition"]
] = None # This is a list of the items being previewed, categorized in the same way as they are in the preview UI.
preview_action_string: t.Optional[
str
] = None # If the preview has an associated action (like "Open"), this will be the localized string for that action.
preview_vendor_hash: t.Optional[
int
] = None # If the preview data is derived from a fake "Preview" Vendor, this will be the hash identifier for the DestinyVendorDefinition of that fake vendor.
screen_style: t.Optional[
str
] = None # A string that the game UI uses as a hint for which detail screen to show for the item. You, too, can leverage this for your own custom screen detail views. Note, however, that these are arbitrarily defined by designers: there's no guarantees of a fixed, known number of these - so fall back to something reasonable if you don't recognize it.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"screenStyle": to_json(self.screen_style),
"previewVendorHash": to_json(self.preview_vendor_hash),
"artifactHash": to_json(self.artifact_hash),
"previewActionString": to_json(self.preview_action_string),
"derivedItemCategories": to_json(self.derived_item_categories),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorDefinition:
"""These are the definitions for Vendors.
In Destiny, a Vendor can be a lot of things - some things that you wouldn't expect, and some things that you don't even see directly in the game. Vendors are the Dolly Levi of the Destiny universe.
- Traditional Vendors as you see in game: people who you come up to and who give you quests, rewards, or who you can buy things from.
- Kiosks/Collections, which are really just Vendors that don't charge currency (or charge some pittance of a currency) and whose gating for purchases revolves more around your character's state.
- Previews for rewards or the contents of sacks. These are implemented as Vendors, where you can't actually purchase from them but the items that they have for sale and the categories of sale items reflect the rewards or contents of the sack. This is so that the game could reuse the existing Vendor display UI for rewards and save a bunch of wheel reinvention.
- Item Transfer capabilities, like the Vault and Postmaster. Vendors can have "acceptedItem" buckets that determine the source and destination buckets for transfers. When you interact with such a vendor, these buckets are what gets shown in the UI instead of any items that the Vendor would have for sale. Yep, the Vault is a vendor.
It is pretty much guaranteed that they'll be used for even more features in the future. They have come to be seen more as generic categorized containers for items than "vendors" in a traditional sense, for better or worse.
Where possible and time allows, we'll attempt to split those out into their own more digestible derived "Definitions": but often time does not allow that, as you can see from the above ways that vendors are used which we never split off from Vendor Definitions externally.
Since Vendors are so many things to so many parts of the game, the definition is understandably complex. You will want to combine this data with live Vendor information from the API when it is available.
"""
accepted_items: t.Optional[
t.Sequence["DestinyVendorAcceptedItemDefinition"]
] = None # If the Vendor is actually a vehicle for the transferring of items (like the Vault and Postmaster vendors), this defines the list of source->destination buckets for transferring.
actions: t.Optional[
t.Sequence["DestinyVendorActionDefinition"]
] = None # Describes "actions" that can be performed on a vendor. Currently, none of these exist. But theoretically a Vendor could let you interact with it by performing actions. We'll see what these end up looking like if they ever get used.
buy_string: t.Optional[
str
] = None # If the vendor has a custom localized string describing the "buy" action, that is returned here.
categories: t.Optional[
t.Sequence["DestinyVendorCategoryEntryDefinition"]
] = None # These are the headers for sections of items that the vendor is selling. When you see items organized by category in the header, it is these categories that it is showing. Well, technically not *exactly* these. On BNet, it doesn't make sense to have categories be "paged" as we do in Destiny, so we run some heuristics to attempt to aggregate pages of categories together. These are the categories post-concatenation, if the vendor had concatenation applied. If you want the pre-aggregated category data, use originalCategories.
consolidate_categories: t.Optional[
bool
] = None # If TRUE, consolidate categories that only differ by trivial properties (such as having minor differences in name)
display_categories: t.Optional[
t.Sequence["DestinyDisplayCategoryDefinition"]
] = None # Display Categories are different from "categories" in that these are specifically for visual grouping and display of categories in Vendor UI. The "categories" structure is for validation of the contained items, and can be categorized entirely separately from "Display Categories", there need be and often will be no meaningful relationship between the two.
display_item_hash: t.Optional[
int
] = None # If the vendor has an item that should be displayed as the "featured" item, this is the hash identifier for that DestinyVendorItemDefinition. Apparently this is usually a related currency, like a reputation token. But it need not be restricted to that.
display_properties: t.Optional["DestinyVendorDisplayPropertiesDefinition"] = None
enabled: t.Optional[
bool
] = None # If a vendor is not enabled, we won't even save the vendor's definition, and we won't return any items or info about them. It's as if they don't exist.
faction_hash: t.Optional[
int
] = None # If the Vendor has a faction, this hash will be valid and point to a DestinyFactionDefinition. The game UI and BNet often mine the faction definition for additional elements and details to place on the screen, such as the faction's Progression status (aka "Reputation").
failure_strings: t.Optional[
t.Sequence[str]
] = None # If an item can't be purchased from the vendor, there may be many "custom"/game state specific reasons why not. This is a list of localized strings with messages for those custom failures. The live BNet data will return a failureIndexes property for items that can't be purchased: using those values to index into this array, you can show the user the appropriate failure message for the item that can't be bought.
groups: t.Optional[
t.Sequence["DestinyVendorGroupReference"]
] = None # A vendor can be a part of 0 or 1 "groups" at a time: a group being a collection of Vendors related by either location or function/purpose. It's used for our our Companion Vendor UI. Only one of these can be active for a Vendor at a time.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
ignore_sale_item_hashes: t.Optional[
t.Sequence[int]
] = None # Some items don't make sense to return in the API, for example because they represent an action to be performed rather than an item being sold. I'd rather we not do this, but at least in the short term this is a workable workaround.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
inhibit_buying: t.Optional[
bool
] = None # If this is true, you aren't allowed to buy whatever the vendor is selling.
inhibit_selling: t.Optional[
bool
] = None # If this is true, you're not allowed to sell whatever the vendor is buying.
interactions: t.Optional[
t.Sequence["DestinyVendorInteractionDefinition"]
] = None # In addition to selling items, vendors can have "interactions": UI where you "talk" with the vendor and they offer you a reward, some item, or merely acknowledge via dialog that you did something cool.
inventory_flyouts: t.Optional[
t.Sequence["DestinyVendorInventoryFlyoutDefinition"]
] = None # If the vendor shows you items from your own inventory - such as the Vault vendor does - this data describes the UI around showing those inventory buckets and which ones get shown.
item_list: t.Optional[
t.Sequence["DestinyVendorItemDefinition"]
] = None # If the vendor sells items (or merely has a list of items to show like the "Sack" vendors do), this is the list of those items that the vendor can sell. From this list, only a subset will be available from the vendor at any given time, selected randomly and reset on the vendor's refresh interval. Note that a vendor can sell the same item multiple ways: for instance, nothing stops a vendor from selling you some specific weapon but using two different currencies, or the same weapon at multiple "item levels".
locations: t.Optional[
t.Sequence["DestinyVendorLocationDefinition"]
] = None # A vendor can be at different places in the world depending on the game/character/account state. This is the list of possible locations for the vendor, along with conditions we use to determine which one is currently active.
original_categories: t.Optional[
t.Sequence["DestinyVendorCategoryEntryDefinition"]
] = None # See the categories property for a description of categories and why originalCategories exists.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
reset_interval_minutes: t.Optional[
int
] = None # A number used for calculating the frequency of a vendor's inventory resetting/refreshing. Don't worry about calculating this - we do it on the server side and send you the next refresh date with the live data.
reset_offset_minutes: t.Optional[
int
] = None # Again, used for reset/refreshing of inventory. Don't worry too much about it. Unless you want to.
return_with_vendor_request: t.Optional[
bool
] = None # As many of you know, Vendor data has historically been pretty brutal on the BNet servers. In an effort to reduce this workload, only Vendors with this flag set will be returned on Vendor requests. This allows us to filter out Vendors that don't dynamic data that's particularly useful: things like "Preview/Sack" vendors, for example, that you can usually suss out the details for using just the definitions themselves.
sell_string: t.Optional[
str
] = None # Ditto for selling. Not that you can sell items to a vendor anymore. Will it come back? Who knows. The string's still there.
services: t.Optional[
t.Sequence["DestinyVendorServiceDefinition"]
] = None # BNet doesn't use this data yet, but it appears to be an optional list of flavor text about services that the Vendor can provide.
unlock_ranges: t.Optional[
t.Sequence["DateRange"]
] = None # If we were able to predict the dates when this Vendor will be visible/available, this will be the list of those date ranges. Sadly, we're not able to predict this very frequently, so this will often be useless data.
vendor_banner: t.Optional[
str
] = None # If the vendor has a custom banner image, that can be found here.
vendor_identifier: t.Optional[
str
] = None # The internal identifier for the Vendor. A holdover from the old days of Vendors, but we don't have time to refactor it away.
vendor_portrait: t.Optional[
str
] = None # A portrait of the Vendor's smiling mug. Or frothing tentacles.
vendor_progression_type: t.Optional[
"DestinyVendorProgressionType"
] = None # The type of reward progression that this vendor has. Default - The original rank progression from token redemption. Ritual - Progression from ranks in ritual content. For example: Crucible (Shaxx), Gambit (Drifter), and Battlegrounds (War Table).
vendor_subcategory_identifier: t.Optional[
str
] = None # The identifier of the VendorCategoryDefinition for this vendor's subcategory.
visible: t.Optional[
bool
] = None # If a vendor is not visible, we still have and will give vendor definition info, but we won't use them for things like Advisors or UI.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"vendorProgressionType": to_json(self.vendor_progression_type),
"buyString": to_json(self.buy_string),
"sellString": to_json(self.sell_string),
"displayItemHash": to_json(self.display_item_hash),
"inhibitBuying": to_json(self.inhibit_buying),
"inhibitSelling": to_json(self.inhibit_selling),
"factionHash": to_json(self.faction_hash),
"resetIntervalMinutes": to_json(self.reset_interval_minutes),
"resetOffsetMinutes": to_json(self.reset_offset_minutes),
"failureStrings": to_json(self.failure_strings),
"unlockRanges": to_json(self.unlock_ranges),
"vendorIdentifier": to_json(self.vendor_identifier),
"vendorPortrait": to_json(self.vendor_portrait),
"vendorBanner": to_json(self.vendor_banner),
"enabled": to_json(self.enabled),
"visible": to_json(self.visible),
"vendorSubcategoryIdentifier": to_json(self.vendor_subcategory_identifier),
"consolidateCategories": to_json(self.consolidate_categories),
"actions": to_json(self.actions),
"categories": to_json(self.categories),
"originalCategories": to_json(self.original_categories),
"displayCategories": to_json(self.display_categories),
"interactions": to_json(self.interactions),
"inventoryFlyouts": to_json(self.inventory_flyouts),
"itemList": to_json(self.item_list),
"services": to_json(self.services),
"acceptedItems": to_json(self.accepted_items),
"returnWithVendorRequest": to_json(self.return_with_vendor_request),
"locations": to_json(self.locations),
"groups": to_json(self.groups),
"ignoreSaleItemHashes": to_json(self.ignore_sale_item_hashes),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorDisplayPropertiesDefinition:
description: t.Optional[str] = None
has_icon: t.Optional[bool] = None
high_res_icon: t.Optional[
str
] = None # If this item has a high-res icon (at least for now, many things won't), then the path to that icon will be here.
icon: t.Optional[
str
] = None # Note that "icon" is sometimes misleading, and should be interpreted in the context of the entity. For instance, in Destiny 1 the DestinyRecordBookDefinition's icon was a big picture of a book. But usually, it will be a small square image that you can use as... well, an icon. They are currently represented as 96px x 96px images.
icon_sequences: t.Optional[t.Sequence["DestinyIconSequenceDefinition"]] = None
large_icon: t.Optional[
str
] = None # I regret calling this a "large icon". It's more like a medium-sized image with a picture of the vendor's mug on it, trying their best to look cool. Not what one would call an icon.
large_transparent_icon: t.Optional[
str
] = None # This is apparently the "Watermark". I am not certain offhand where this is actually used in the Game UI, but some people may find it useful.
map_icon: t.Optional[
str
] = None # This is the icon used in the map overview, when the vendor is located on the map.
name: t.Optional[str] = None
original_icon: t.Optional[
str
] = None # If we replaced the icon with something more glitzy, this is the original icon that the vendor had according to the game's content. It may be more lame and/or have less razzle-dazzle. But who am I to tell you which icon to use.
requirements_display: t.Optional[
t.Sequence["DestinyVendorRequirementDisplayEntryDefinition"]
] = None # Vendors, in addition to expected display property data, may also show some "common requirements" as statically defined definition data. This might be when a vendor accepts a single type of currency, or when the currency is unique to the vendor and the designers wanted to show that currency when you interact with the vendor.
small_transparent_icon: t.Optional[
str
] = None # This is the icon used in parts of the game UI such as the vendor's waypoint.
subtitle: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"largeIcon": to_json(self.large_icon),
"subtitle": to_json(self.subtitle),
"originalIcon": to_json(self.original_icon),
"requirementsDisplay": to_json(self.requirements_display),
"smallTransparentIcon": to_json(self.small_transparent_icon),
"mapIcon": to_json(self.map_icon),
"largeTransparentIcon": to_json(self.large_transparent_icon),
"description": to_json(self.description),
"name": to_json(self.name),
"icon": to_json(self.icon),
"iconSequences": to_json(self.icon_sequences),
"highResIcon": to_json(self.high_res_icon),
"hasIcon": to_json(self.has_icon),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorRequirementDisplayEntryDefinition:
"""The localized properties of the requirementsDisplay, allowing
information about the requirement or item being featured to be seen."""
icon: t.Optional[str] = None
name: t.Optional[str] = None
source: t.Optional[str] = None
type: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"icon": to_json(self.icon),
"name": to_json(self.name),
"source": to_json(self.source),
"type": to_json(self.type),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorActionDefinition:
"""If a vendor can ever end up performing actions, these are the properties
that will be related to those actions.
I'm not going to bother documenting this yet, as it is unused and
unclear if it will ever be used... but in case it is ever populated
and someone finds it useful, it is defined here.
"""
action_hash: t.Optional[int] = None
action_id: t.Optional[str] = None
auto_perform_action: t.Optional[bool] = None
description: t.Optional[str] = None
execute_seconds: t.Optional[int] = None
icon: t.Optional[str] = None
is_positive: t.Optional[bool] = None
name: t.Optional[str] = None
verb: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"description": to_json(self.description),
"executeSeconds": to_json(self.execute_seconds),
"icon": to_json(self.icon),
"name": to_json(self.name),
"verb": to_json(self.verb),
"isPositive": to_json(self.is_positive),
"actionId": to_json(self.action_id),
"actionHash": to_json(self.action_hash),
"autoPerformAction": to_json(self.auto_perform_action),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorCategoryEntryDefinition:
"""This is the definition for a single Vendor Category, into which Sale
Items are grouped."""
buy_string_override: t.Optional[
str
] = None # The localized string for making purchases from this category, if it is different from the vendor's string for purchasing.
category_hash: t.Optional[int] = None # The hashed identifier for the category.
category_index: t.Optional[
int
] = None # The index of the category in the original category definitions for the vendor.
disabled_description: t.Optional[
str
] = None # If the category is disabled, this is the localized description to show.
display_title: t.Optional[str] = None # The localized title of the category.
hide_from_regular_purchase: t.Optional[
bool
] = None # True if this category doesn't allow purchases.
hide_if_no_currency: t.Optional[
bool
] = None # If you don't have the currency required to buy items from this category, should the items be hidden?
is_display_only: t.Optional[
bool
] = None # If true, this category only displays items: you can't purchase anything in them.
is_preview: t.Optional[
bool
] = None # Sometimes a category isn't actually used to sell items, but rather to preview them. This implies different UI (and manual placement of the category in the UI) in the game, and special treatment.
overlay: t.Optional[
"DestinyVendorCategoryOverlayDefinition"
] = None # If this category has an overlay prompt that should appear, this contains the details of that prompt.
quantity_available: t.Optional[
int
] = None # The amount of items that will be available when this category is shown.
reset_interval_minutes_override: t.Optional[int] = None
reset_offset_minutes_override: t.Optional[int] = None
show_unavailable_items: t.Optional[
bool
] = None # If items aren't up for sale in this category, should we still show them (greyed out)?
sort_value: t.Optional[
int
] = None # Used in sorting items in vendors... but there's a lot more to it. Just go with the order provided in the itemIndexes property on the DestinyVendorCategoryComponent instead, it should be more reliable than trying to recalculate it yourself.
vendor_item_indexes: t.Optional[
t.Sequence[int]
] = None # A shortcut for the vendor item indexes sold under this category. Saves us from some expensive reorganization at runtime.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"categoryIndex": to_json(self.category_index),
"sortValue": to_json(self.sort_value),
"categoryHash": to_json(self.category_hash),
"quantityAvailable": to_json(self.quantity_available),
"showUnavailableItems": to_json(self.show_unavailable_items),
"hideIfNoCurrency": to_json(self.hide_if_no_currency),
"hideFromRegularPurchase": to_json(self.hide_from_regular_purchase),
"buyStringOverride": to_json(self.buy_string_override),
"disabledDescription": to_json(self.disabled_description),
"displayTitle": to_json(self.display_title),
"overlay": to_json(self.overlay),
"vendorItemIndexes": to_json(self.vendor_item_indexes),
"isPreview": to_json(self.is_preview),
"isDisplayOnly": to_json(self.is_display_only),
"resetIntervalMinutesOverride": to_json(
self.reset_interval_minutes_override
),
"resetOffsetMinutesOverride": to_json(self.reset_offset_minutes_override),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorCategoryOverlayDefinition:
"""The details of an overlay prompt to show to a user.
They are all fairly self-explanatory localized strings that can be
shown.
"""
choice_description: t.Optional[str] = None
currency_item_hash: t.Optional[
int
] = None # If this overlay has a currency item that it features, this is said featured item.
description: t.Optional[str] = None
icon: t.Optional[str] = None
title: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"choiceDescription": to_json(self.choice_description),
"description": to_json(self.description),
"icon": to_json(self.icon),
"title": to_json(self.title),
"currencyItemHash": to_json(self.currency_item_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyDisplayCategoryDefinition:
"""Display Categories are different from "categories" in that these are
specifically for visual grouping and display of categories in Vendor UI.
The "categories" structure is for validation of the contained items,
and can be categorized entirely separately from "Display
Categories", there need be and often will be no meaningful
relationship between the two.
"""
display_category_hash: t.Optional[int] = None
display_in_banner: t.Optional[
bool
] = None # If true, this category should be displayed in the "Banner" section of the vendor's UI.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
display_style_hash: t.Optional[
int
] = None # An indicator of how the category will be displayed in the UI. It's up to you to do something cool or interesting in response to this, or just to treat it as a normal category.
display_style_identifier: t.Optional[
str
] = None # An indicator of how the category will be displayed in the UI. It's up to you to do something cool or interesting in response to this, or just to treat it as a normal category.
identifier: t.Optional[str] = None # A string identifier for the display category.
index: t.Optional[int] = None
progression_hash: t.Optional[
int
] = None # If it exists, this is the hash identifier of a DestinyProgressionDefinition that represents the progression to show on this display category. Specific categories can now have thier own distinct progression, apparently. So that's cool.
sort_order: t.Optional[
"VendorDisplayCategorySortOrder"
] = None # If this category sorts items in a nonstandard way, this will be the way we sort.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"index": to_json(self.index),
"identifier": to_json(self.identifier),
"displayCategoryHash": to_json(self.display_category_hash),
"displayProperties": to_json(self.display_properties),
"displayInBanner": to_json(self.display_in_banner),
"progressionHash": to_json(self.progression_hash),
"sortOrder": to_json(self.sort_order),
"displayStyleHash": to_json(self.display_style_hash),
"displayStyleIdentifier": to_json(self.display_style_identifier),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorInteractionDefinition:
"""A Vendor Interaction is a dialog shown by the vendor other than sale
items or transfer screens.
The vendor is showing you something, and asking you to reply to it
by choosing an option or reward.
"""
flavor_line_one: t.Optional[
str
] = None # If the vendor interaction has flavor text, this is some of it.
flavor_line_two: t.Optional[
str
] = None # If the vendor interaction has flavor text, this is the rest of it.
header_display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The header for the interaction dialog.
instructions: t.Optional[
str
] = None # The localized text telling the player what to do when they see this dialog.
interaction_index: t.Optional[
int
] = None # The position of this interaction in its parent array. Note that this is NOT content agnostic, and should not be used as such.
interaction_type: t.Optional[
"VendorInteractionType"
] = None # The enumerated version of the possible UI hints for vendor interactions, which is a little easier to grok than the hash found in uiInteractionType.
questline_item_hash: t.Optional[
int
] = None # If this interaction dialog is about a quest, this is the questline related to the interaction. You can use this to show the quest overview, or even the character's status with the quest if you use it to find the character's current Quest Step by checking their inventory against this questlineItemHash's DestinyInventoryItemDefinition.setData.
replies: t.Optional[
t.Sequence["DestinyVendorInteractionReplyDefinition"]
] = None # The potential replies that the user can make to the interaction.
reward_block_label: t.Optional[
str
] = None # If this interaction is displaying rewards, this is the text to use for the header of the reward-displaying section of the interaction.
reward_vendor_category_index: t.Optional[
int
] = None # If the vendor's reward list is sourced from one of his categories, this is the index into the category array of items to show.
sack_interaction_list: t.Optional[
t.Sequence["DestinyVendorInteractionSackEntryDefinition"]
] = None # If this interaction is meant to show you sacks, this is the list of types of sacks to be shown. If empty, the interaction is not meant to show sacks.
ui_interaction_type: t.Optional[
int
] = None # A UI hint for the behavior of the interaction screen. This is useful to determine what type of interaction is occurring, such as a prompt to receive a rank up reward or a prompt to choose a reward for completing a quest. The hash isn't as useful as the Enum in retrospect, well what can you do. Try using interactionType instead.
vendor_category_index: t.Optional[
int
] = None # If >= 0, this is the category of sale items to show along with this interaction dialog.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"interactionIndex": to_json(self.interaction_index),
"replies": to_json(self.replies),
"vendorCategoryIndex": to_json(self.vendor_category_index),
"questlineItemHash": to_json(self.questline_item_hash),
"sackInteractionList": to_json(self.sack_interaction_list),
"uiInteractionType": to_json(self.ui_interaction_type),
"interactionType": to_json(self.interaction_type),
"rewardBlockLabel": to_json(self.reward_block_label),
"rewardVendorCategoryIndex": to_json(self.reward_vendor_category_index),
"flavorLineOne": to_json(self.flavor_line_one),
"flavorLineTwo": to_json(self.flavor_line_two),
"headerDisplayProperties": to_json(self.header_display_properties),
"instructions": to_json(self.instructions),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorInteractionReplyDefinition:
"""When the interaction is replied to, Reward sites will fire and items
potentially selected based on whether the given unlock expression is TRUE.
You can potentially choose one from multiple replies when replying
to an interaction: this is how you get either/or rewards from
vendors.
"""
item_rewards_selection: t.Optional[
"DestinyVendorInteractionRewardSelection"
] = None # The rewards granted upon responding to the vendor.
reply: t.Optional[str] = None # The localized text for the reply.
reply_type: t.Optional[
"DestinyVendorReplyType"
] = None # An enum indicating the type of reply being made.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"itemRewardsSelection": to_json(self.item_rewards_selection),
"reply": to_json(self.reply),
"replyType": to_json(self.reply_type),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorInteractionSackEntryDefinition:
"""Compare this sackType to the sack identifier in the
DestinyInventoryItemDefinition.vendorSackType property of items.
If they match, show this sack with this interaction.
"""
sack_type: t.Optional[int] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"sackType": to_json(self.sack_type),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorInventoryFlyoutDefinition:
"""The definition for an "inventory flyout": a UI screen where we show you
part of an otherwise hidden vendor inventory: like the Vault inventory
buckets."""
buckets: t.Optional[
t.Sequence["DestinyVendorInventoryFlyoutBucketDefinition"]
] = None # A list of inventory buckets and other metadata to show on the screen.
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The title and other common properties of the flyout.
equipment_slot_hash: t.Optional[
int
] = None # If this flyout is meant to show you the contents of the player's equipment slot, this is the slot to show.
flyout_id: t.Optional[
int
] = None # An identifier for the flyout, in case anything else needs to refer to them.
locked_description: t.Optional[
str
] = None # If the flyout is locked, this is the reason why.
suppress_newness: t.Optional[
bool
] = None # If this is true, don't show any of the glistening "this is a new item" UI elements, like we show on the inventory items themselves in in-game UI.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"lockedDescription": to_json(self.locked_description),
"displayProperties": to_json(self.display_properties),
"buckets": to_json(self.buckets),
"flyoutId": to_json(self.flyout_id),
"suppressNewness": to_json(self.suppress_newness),
"equipmentSlotHash": to_json(self.equipment_slot_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorInventoryFlyoutBucketDefinition:
"""Information about a single inventory bucket in a vendor flyout UI and
how it is shown."""
collapsible: t.Optional[
bool
] = None # If true, the inventory bucket should be able to be collapsed visually.
inventory_bucket_hash: t.Optional[
int
] = None # The inventory bucket whose contents should be shown.
sort_items_by: t.Optional[
"DestinyItemSortType"
] = None # The methodology to use for sorting items from the flyout.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"collapsible": to_json(self.collapsible),
"inventoryBucketHash": to_json(self.inventory_bucket_hash),
"sortItemsBy": to_json(self.sort_items_by),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorItemDefinition:
"""This represents an item being sold by the vendor."""
action: t.Optional[
"DestinyVendorSaleItemActionBlockDefinition"
] = None # The action to be performed when purchasing the item, if it's not just "buy".
category_index: t.Optional[
int
] = None # The index into the DestinyVendorDefinition.categories array, so you can find the category associated with this item.
creation_levels: t.Optional[
t.Sequence["DestinyItemCreationEntryLevelDefinition"]
] = None # The Default level at which the item will spawn. Almost always driven by an adjusto these days. Ideally should be singular. It's a long story how this ended up as a list, but there is always either going to be 0:1 of these entities.
currencies: t.Optional[
t.Sequence["DestinyVendorItemQuantity"]
] = None # This is a pre-compiled aggregation of item value and priceOverrideList, so that we have one place to check for what the purchaser must pay for the item. Use this instead of trying to piece together the price separately. The somewhat crappy part about this is that, now that item quantity overrides have dynamic modifiers, this will not necessarily be statically true. If you were using this instead of live data, switch to using live data.
display_category: t.Optional[
str
] = None # The string identifier for the category selling this item.
display_category_index: t.Optional[
int
] = None # This is an index specifically into the display category, as opposed to the server-side Categories (which do not need to match or pair with each other in any way: server side categories are really just structures for common validation. Display Category will let us more easily categorize items visually)
exclusivity: t.Optional[
"BungieMembershipType"
] = None # If this item can only be purchased by a given platform, this indicates the platform to which it is restricted.
expiration_tooltip: t.Optional[
str
] = None # If this item can expire, this is the tooltip message to show with its expiration info.
failure_indexes: t.Optional[
t.Sequence[int]
] = None # An list of indexes into the DestinyVendorDefinition.failureStrings array, indicating the possible failure strings that can be relevant for this item.
inventory_bucket_hash: t.Optional[
int
] = None # The inventory bucket into which this item will be placed upon purchase.
is_crm: t.Optional[
bool
] = None # If this sale can only be performed as the result of receiving a CRM offer, this is true.
is_offer: t.Optional[
bool
] = None # If this sale can only be performed as the result of an offer check, this is true.
item_hash: t.Optional[
int
] = None # The hash identifier of the item being sold (DestinyInventoryItemDefinition). Note that a vendor can sell the same item in multiple ways, so don't assume that itemHash is a unique identifier for this entity.
maximum_level: t.Optional[
int
] = None # The maximum character level at which this item is available for sale.
minimum_level: t.Optional[
int
] = None # The minimum character level at which this item is available for sale.
original_category_index: t.Optional[
int
] = None # Same as above, but for the original category indexes.
purchasable_scope: t.Optional[
"DestinyGatingScope"
] = None # Similar to visibilityScope, it represents the most restrictive scope that determines whether the item can be purchased. It will at least be as restrictive as visibilityScope, but could be more restrictive if the item has additional purchase requirements beyond whether it is merely visible or not. See DestinyGatingScope's documentation for more information.
quantity: t.Optional[
int
] = None # The amount you will recieve of the item described in itemHash if you make the purchase.
redirect_to_sale_indexes: t.Optional[
t.Sequence[int]
] = None # If this is populated, the purchase of this item should redirect to purchasing these other items instead.
refund_policy: t.Optional[
"DestinyVendorItemRefundPolicy"
] = None # If this item can be refunded, this is the policy for what will be refundd, how, and in what time period.
refund_time_limit: t.Optional[
int
] = None # The amount of time before refundability of the newly purchased item will expire.
socket_overrides: t.Optional[t.Sequence["DestinyVendorItemSocketOverride"]] = None
sort_value: t.Optional[
int
] = None # *if* the category this item is in supports non-default sorting, this value should represent the sorting value to use, pre-processed and ready to go.
unpurchasable: t.Optional[
bool
] = None # If true, this item is some sort of dummy sale item that cannot actually be purchased. It may be a display only item, or some fluff left by a content designer for testing purposes, or something that got disabled because it was a terrible idea. You get the picture. We won't know *why* it can't be purchased, only that it can't be. Sorry. This is also only whether it's unpurchasable as a static property according to game content. There are other reasons why an item may or may not be purchasable at runtime, so even if this isn't set to True you should trust the runtime value for this sale item over the static definition if this is unset.
vendor_item_index: t.Optional[
int
] = None # The index into the DestinyVendorDefinition.saleList. This is what we use to refer to items being sold throughout live and definition data.
visibility_scope: t.Optional[
"DestinyGatingScope"
] = None # The most restrictive scope that determines whether the item is available in the Vendor's inventory. See DestinyGatingScope's documentation for more information. This can be determined by Unlock gating, or by whether or not the item has purchase level requirements (minimumLevel and maximumLevel properties).
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"vendorItemIndex": to_json(self.vendor_item_index),
"itemHash": to_json(self.item_hash),
"quantity": to_json(self.quantity),
"failureIndexes": to_json(self.failure_indexes),
"currencies": to_json(self.currencies),
"refundPolicy": to_json(self.refund_policy),
"refundTimeLimit": to_json(self.refund_time_limit),
"creationLevels": to_json(self.creation_levels),
"displayCategoryIndex": to_json(self.display_category_index),
"categoryIndex": to_json(self.category_index),
"originalCategoryIndex": to_json(self.original_category_index),
"minimumLevel": to_json(self.minimum_level),
"maximumLevel": to_json(self.maximum_level),
"action": to_json(self.action),
"displayCategory": to_json(self.display_category),
"inventoryBucketHash": to_json(self.inventory_bucket_hash),
"visibilityScope": to_json(self.visibility_scope),
"purchasableScope": to_json(self.purchasable_scope),
"exclusivity": to_json(self.exclusivity),
"isOffer": to_json(self.is_offer),
"isCrm": to_json(self.is_crm),
"sortValue": to_json(self.sort_value),
"expirationTooltip": to_json(self.expiration_tooltip),
"redirectToSaleIndexes": to_json(self.redirect_to_sale_indexes),
"socketOverrides": to_json(self.socket_overrides),
"unpurchasable": to_json(self.unpurchasable),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorItemQuantity:
"""In addition to item quantity information for vendor prices, this also
has any optional information that may exist about how the item's quantity
can be modified.
(unfortunately not information that is able to be read outside of
the BNet servers, but it's there)
"""
has_conditional_visibility: t.Optional[
bool
] = None # Indicates that this item quantity may be conditionally shown or hidden, based on various sources of state. For example: server flags, account state, or character progress.
item_hash: t.Optional[
int
] = None # The hash identifier for the item in question. Use it to look up the item's DestinyInventoryItemDefinition.
item_instance_id: t.Optional[
int
] = None # If this quantity is referring to a specific instance of an item, this will have the item's instance ID. Normally, this will be null.
quantity: t.Optional[
int
] = None # The amount of the item needed/available depending on the context of where DestinyItemQuantity is being used.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"itemHash": to_json(self.item_hash),
"itemInstanceId": to_json(self.item_instance_id),
"quantity": to_json(self.quantity),
"hasConditionalVisibility": to_json(self.has_conditional_visibility),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemCreationEntryLevelDefinition:
"""An overly complicated wrapper for the item level at which the item
should spawn."""
level: t.Optional[int] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"level": to_json(self.level),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorSaleItemActionBlockDefinition:
"""Not terribly useful, some basic cooldown interaction info."""
execute_seconds: t.Optional[float] = None
is_positive: t.Optional[bool] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"executeSeconds": to_json(self.execute_seconds),
"isPositive": to_json(self.is_positive),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorItemSocketOverride:
"""The information for how the vendor purchase should override a given
socket with custom plug data."""
randomized_options_count: t.Optional[
int
] = None # If this is greater than -1, the number of randomized plugs on this socket will be set to this quantity instead of whatever it's set to by default.
single_item_hash: t.Optional[
int
] = None # If this is populated, the socket will be overridden with a specific plug. If this isn't populated, it's being overridden by something more complicated that is only known by the Game Server and God, which means we can't tell you in advance what it'll be.
socket_type_hash: t.Optional[
int
] = None # This appears to be used to select which socket ultimately gets the override defined here.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"singleItemHash": to_json(self.single_item_hash),
"randomizedOptionsCount": to_json(self.randomized_options_count),
"socketTypeHash": to_json(self.socket_type_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorServiceDefinition:
"""When a vendor provides services, this is the localized name of those
services."""
name: t.Optional[str] = None # The localized name of a service provided.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"name": to_json(self.name),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorAcceptedItemDefinition:
"""If you ever wondered how the Vault works, here it is.
The Vault is merely a set of inventory buckets that exist on your
Profile/Account level. When you transfer items in the Vault, the
game is using the Vault Vendor's
DestinyVendorAcceptedItemDefinitions to see where the appropriate
destination bucket is for the source bucket from whence your item is
moving. If it finds such an entry, it transfers the item to the
other bucket. The mechanics for Postmaster works similarly, which is
also a vendor. All driven by Accepted Items.
"""
accepted_inventory_bucket_hash: t.Optional[
int
] = None # The "source" bucket for a transfer. When a user wants to transfer an item, the appropriate DestinyVendorDefinition's acceptedItems property is evaluated, looking for an entry where acceptedInventoryBucketHash matches the bucket that the item being transferred is currently located. If it exists, the item will be transferred into whatever bucket is defined by destinationInventoryBucketHash.
destination_inventory_bucket_hash: t.Optional[
int
] = None # This is the bucket where the item being transferred will be put, given that it was being transferred *from* the bucket defined in acceptedInventoryBucketHash.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"acceptedInventoryBucketHash": to_json(self.accepted_inventory_bucket_hash),
"destinationInventoryBucketHash": to_json(
self.destination_inventory_bucket_hash
),
}
[docs]@dt.dataclass(frozen=True)
class DestinyDestinationDefinition:
"""On to one of the more confusing subjects of the API.
What is a Destination, and what is the relationship between it,
Activities, Locations, and Places? A "Destination" is a specific
region/city/area of a larger "Place". For instance, a Place might be
Earth where a Destination might be Bellevue, Washington. (Please,
pick a more interesting destination if you come to visit Earth).
"""
activity_graph_entries: t.Optional[
t.Sequence["DestinyActivityGraphListEntryDefinition"]
] = None # If the Destination has default Activity Graphs (i.e. "Map") that should be shown in the director, this is the list of those Graphs. At most, only one should be active at any given time for a Destination: these would represent, for example, different variants on a Map if the Destination is changing on a macro level based on game state.
bubble_settings: t.Optional[
t.Sequence["DestinyDestinationBubbleSettingDefinition"]
] = None # A Destination may have many "Bubbles" zones with human readable properties. We don't get as much info as I'd like about them - I'd love to return info like where on the map they are located - but at least this gives you the name of those bubbles. bubbleSettings and bubbles both have the identical number of entries, and you should match up their indexes to provide matching bubble and bubbleSettings data. DEPRECATED - Just use bubbles, it now has this data.
bubbles: t.Optional[
t.Sequence["DestinyBubbleDefinition"]
] = None # This provides the unique identifiers for every bubble in the destination (only guaranteed unique within the destination), and any intrinsic properties of the bubble. bubbleSettings and bubbles both have the identical number of entries, and you should match up their indexes to provide matching bubble and bubbleSettings data.
default_freeroam_activity_hash: t.Optional[
int
] = None # If this Destination has a default Free-Roam activity, this is the hash for that Activity. Use it to look up the DestinyActivityDefintion.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
place_hash: t.Optional[
int
] = None # The place that "owns" this Destination. Use this hash to look up the DestinyPlaceDefinition.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"placeHash": to_json(self.place_hash),
"defaultFreeroamActivityHash": to_json(self.default_freeroam_activity_hash),
"activityGraphEntries": to_json(self.activity_graph_entries),
"bubbleSettings": to_json(self.bubble_settings),
"bubbles": to_json(self.bubbles),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityGraphListEntryDefinition:
"""Destinations and Activities may have default Activity Graphs that should
be shown when you bring up the Director and are playing in either.
This contract defines the graph referred to and the gating for when
it is relevant.
"""
activity_graph_hash: t.Optional[
int
] = None # The hash identifier of the DestinyActivityGraphDefinition that should be shown when opening the director.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"activityGraphHash": to_json(self.activity_graph_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityDefinition:
"""The static data about Activities in Destiny 2.
Note that an Activity must be combined with an ActivityMode to know - from a Gameplay perspective - what the user is "Playing".
In most PvE activities, this is fairly straightforward. A Story Activity can only be played in the Story Activity Mode.
However, in PvP activities, the Activity alone only tells you the map being played, or the Playlist that the user chose to enter. You'll need to know the Activity Mode they're playing to know that they're playing Mode X on Map Y.
Activity Definitions tell a great deal of information about what *could* be relevant to a user: what rewards they can earn, what challenges could be performed, what modifiers could be applied. To figure out which of these properties is actually live, you'll need to combine the definition with "Live" data from one of the Destiny endpoints.
Activities also have Activity Types, but unfortunately in Destiny 2 these are even less reliable of a source of information than they were in Destiny 1. I will be looking into ways to provide more reliable sources for type information as time goes on, but for now we're going to have to deal with the limitations. See DestinyActivityTypeDefinition for more information.
"""
activity_graph_list: t.Optional[
t.Sequence["DestinyActivityGraphListEntryDefinition"]
] = None # Unfortunately, in practice this is almost never populated. In theory, this is supposed to tell which Activity Graph to show if you bring up the director while in this activity.
activity_light_level: t.Optional[
int
] = None # The recommended light level for this activity.
activity_location_mappings: t.Optional[
t.Sequence["DestinyEnvironmentLocationMapping"]
] = None # A list of location mappings that are affected by this activity. Pulled out of DestinyLocationDefinitions for our/your lookup convenience.
activity_mode_hashes: t.Optional[
t.Sequence[int]
] = None # The hash identifiers for Activity Modes relevant to this activity. Note that if this is a playlist, the specific playlist entry chosen will determine the actual activity modes that end up being relevant.
activity_mode_types: t.Optional[
t.Sequence["DestinyActivityModeType"]
] = None # The activity modes - if any - in enum form. Because we can't seem to escape the enums.
activity_type_hash: t.Optional[
int
] = None # The hash identifier for the Activity Type of this Activity. You may use it to look up the DestinyActivityTypeDefinition for human readable info, but be forewarned: Playlists and many PVP Map Activities will map to generic Activity Types. You'll have to use your knowledge of the Activity Mode being played to get more specific information about what the user is playing.
challenges: t.Optional[
t.Sequence["DestinyActivityChallengeDefinition"]
] = None # An activity can have many Challenges, of which any subset of them may be active for play at any given period of time. This gives the information about the challenges and data that we use to understand when they're active and what rewards they provide. Sadly, at the moment there's no central definition for challenges: much like "Skulls" were in Destiny 1, these are defined on individual activities and there can be many duplicates/near duplicates across the Destiny 2 ecosystem. I have it in mind to centralize these in a future revision of the API, but we are out of time.
destination_hash: t.Optional[
int
] = None # The hash identifier for the Destination on which this Activity is played. Use it to look up the DestinyDestinationDefinition for human readable info about the destination. A Destination can be thought of as a more specific location than a "Place". For instance, if the "Place" is Earth, the "Destination" would be a specific city or region on Earth.
direct_activity_mode_hash: t.Optional[
int
] = None # If this activity had an activity mode directly defined on it, this will be the hash of that mode.
direct_activity_mode_type: t.Optional[
int
] = None # If the activity had an activity mode directly defined on it, this will be the enum value of that mode.
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The title, subtitle, and icon for the activity. We do a little post-processing on this to try and account for Activities where the designers have left this data too minimal to determine what activity is actually being played.
guided_game: t.Optional[
"DestinyActivityGuidedBlockDefinition"
] = None # This block of data, if it exists, provides information about the guided game experience and restrictions for this activity. If it doesn't exist, the game is not able to be played as a guided game.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
insertion_points: t.Optional[
t.Sequence["DestinyActivityInsertionPointDefinition"]
] = None # The list of phases or points of entry into an activity, along with information we can use to determine their gating and availability.
is_playlist: t.Optional[
bool
] = None # If True, this Activity is actually a Playlist that refers to multiple possible specific Activities and Activity Modes. For instance, a Crucible Playlist may have references to multiple Activities (Maps) with multiple Activity Modes (specific PvP gameplay modes). If this is true, refer to the playlistItems property for the specific entries in the playlist.
is_pv_p: t.Optional[
bool
] = None # If true, this activity is a PVP activity or playlist.
loadouts: t.Optional[
t.Sequence["DestinyActivityLoadoutRequirementSet"]
] = None # The set of all possible loadout requirements that could be active for this activity. Only one will be active at any given time, and you can discover which one through activity-associated data such as Milestones that have activity info on them.
matchmaking: t.Optional[
"DestinyActivityMatchmakingBlockDefinition"
] = None # This block of data provides information about the Activity's matchmaking attributes: how many people can join and such.
modifiers: t.Optional[
t.Sequence["DestinyActivityModifierReferenceDefinition"]
] = None # Activities can have Modifiers, as defined in DestinyActivityModifierDefinition. These are references to the modifiers that *can* be applied to that activity, along with data that we use to determine if that modifier is actually active at any given point in time.
optional_unlock_strings: t.Optional[
t.Sequence["DestinyActivityUnlockStringDefinition"]
] = None # If there are status strings related to the activity and based on internal state of the game, account, or character, then this will be the definition of those strings and the states needed in order for the strings to be shown.
original_display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The unadulterated form of the display properties, as they ought to be shown in the Director (if the activity appears in the director).
pgcr_image: t.Optional[
str
] = None # When Activities are completed, we generate a "Post-Game Carnage Report", or PGCR, with details about what happened in that activity (how many kills someone got, which team won, etc...) We use this image as the background when displaying PGCR information, and often use it when we refer to the Activity in general.
place_hash: t.Optional[
int
] = None # The hash identifier for the "Place" on which this Activity is played. Use it to look up the DestinyPlaceDefinition for human readable info about the Place. A Place is the largest-scoped concept for location information. For instance, if the "Place" is Earth, the "Destination" would be a specific city or region on Earth.
playlist_items: t.Optional[
t.Sequence["DestinyActivityPlaylistItemDefinition"]
] = None # Represents all of the possible activities that could be played in the Playlist, along with information that we can use to determine if they are active at the present time.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
release_icon: t.Optional[
str
] = None # If the activity has an icon associated with a specific release (such as a DLC), this is the path to that release's icon.
release_time: t.Optional[
int
] = None # If the activity will not be visible until a specific and known time, this will be the seconds since the Epoch when it will become visible.
rewards: t.Optional[
t.Sequence["DestinyActivityRewardDefinition"]
] = None # The expected possible rewards for the activity. These rewards may or may not be accessible for an individual player based on their character state, the account state, and even the game's state overall. But it is a useful reference for possible rewards you can earn in the activity. These match up to rewards displayed when you hover over the Activity in the in-game Director, and often refer to Placeholder or "Dummy" items: items that tell you what you can earn in vague terms rather than what you'll specifically be earning (partly because the game doesn't even know what you'll earn specifically until you roll for it at the end)
selection_screen_display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The title, subtitle, and icon for the activity as determined by Selection Screen data, if there is any for this activity. There won't be data in this field if the activity is never shown in a selection/options screen.
tier: t.Optional[int] = None # The difficulty tier of the activity.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"originalDisplayProperties": to_json(self.original_display_properties),
"selectionScreenDisplayProperties": to_json(
self.selection_screen_display_properties
),
"releaseIcon": to_json(self.release_icon),
"releaseTime": to_json(self.release_time),
"activityLightLevel": to_json(self.activity_light_level),
"destinationHash": to_json(self.destination_hash),
"placeHash": to_json(self.place_hash),
"activityTypeHash": to_json(self.activity_type_hash),
"tier": to_json(self.tier),
"pgcrImage": to_json(self.pgcr_image),
"rewards": to_json(self.rewards),
"modifiers": to_json(self.modifiers),
"isPlaylist": to_json(self.is_playlist),
"challenges": to_json(self.challenges),
"optionalUnlockStrings": to_json(self.optional_unlock_strings),
"playlistItems": to_json(self.playlist_items),
"activityGraphList": to_json(self.activity_graph_list),
"matchmaking": to_json(self.matchmaking),
"guidedGame": to_json(self.guided_game),
"directActivityModeHash": to_json(self.direct_activity_mode_hash),
"directActivityModeType": to_json(self.direct_activity_mode_type),
"loadouts": to_json(self.loadouts),
"activityModeHashes": to_json(self.activity_mode_hashes),
"activityModeTypes": to_json(self.activity_mode_types),
"isPvP": to_json(self.is_pv_p),
"insertionPoints": to_json(self.insertion_points),
"activityLocationMappings": to_json(self.activity_location_mappings),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityRewardDefinition:
"""Activities can refer to one or more sets of tooltip-friendly reward
data.
These are the definitions for those tooltip friendly rewards.
"""
reward_items: t.Optional[
t.Sequence["DestinyItemQuantity"]
] = None # The "Items provided" in the reward. This is almost always a pointer to a DestinyInventoryItemDefintion for an item that you can't actually earn in-game, but that has name/description/icon information for the vague concept of the rewards you will receive. This is because the actual reward generation is non-deterministic and extremely complicated, so the best the game can do is tell you what you'll get in vague terms. And so too shall we. Interesting trivia: you actually *do* earn these items when you complete the activity. They go into a single-slot bucket on your profile, which is how you see the pop-ups of these rewards when you complete an activity that match these "dummy" items. You can even see them if you look at the last one you earned in your profile-level inventory through the BNet API! Who said reading documentation is a waste of time?
reward_text: t.Optional[str] = None # The header for the reward set, if any.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"rewardText": to_json(self.reward_text),
"rewardItems": to_json(self.reward_items),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityModifierReferenceDefinition:
"""A reference to an Activity Modifier from another entity, such as an
Activity (for now, just Activities).
This defines some
"""
activity_modifier_hash: t.Optional[
int
] = None # The hash identifier for the DestinyActivityModifierDefinition referenced by this activity.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"activityModifierHash": to_json(self.activity_modifier_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityChallengeDefinition:
"""Represents a reference to a Challenge, which for now is just an
Objective."""
dummy_rewards: t.Optional[
t.Sequence["DestinyItemQuantity"]
] = None # The rewards as they're represented in the UI. Note that they generally link to "dummy" items that give a summary of rewards rather than direct, real items themselves. If the quantity is 0, don't show the quantity.
objective_hash: t.Optional[
int
] = None # The hash for the Objective that matches this challenge. Use it to look up the DestinyObjectiveDefinition.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"objectiveHash": to_json(self.objective_hash),
"dummyRewards": to_json(self.dummy_rewards),
}
[docs]@dt.dataclass(frozen=True)
class DestinyObjectiveDefinition:
"""Defines an "Objective". An objective is a specific task you should
accomplish in the game. These are referred to by:
- Quest Steps (which are DestinyInventoryItemDefinition entities with Objectives)
- Challenges (which are Objectives defined on an DestinyActivityDefintion)
- Milestones (which refer to Objectives that are defined on both Quest Steps and Activities)
- Anything else that the designers decide to do later.
Objectives have progress, a notion of having been Completed, human readable data describing the task to be accomplished, and a lot of optional tack-on data that can enhance the information provided about the task.
"""
allow_negative_value: t.Optional[
bool
] = None # If true, the value is allowed to go negative.
allow_overcompletion: t.Optional[
bool
] = None # If True, the progress will continue even beyond the point where the objective met its minimum completion requirements. Your UI will have to accommodate it.
allow_value_change_when_completed: t.Optional[
bool
] = None # If true, you can effectively "un-complete" this objective if you lose progress after crossing the completion threshold. If False, once you complete the task it will remain completed forever by locking the value.
completed_value_style: t.Optional[
"DestinyUnlockValueUIStyle"
] = None # The style to use when the objective is completed.
completion_value: t.Optional[
int
] = None # The value that the unlock value defined in unlockValueHash must reach in order for the objective to be considered Completed. Used in calculating progress and completion status.
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # Ideally, this should tell you what your task is. I'm not going to lie to you though. Sometimes this doesn't have useful information at all. Which sucks, but there's nothing either of us can do about it.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
in_progress_value_style: t.Optional[
"DestinyUnlockValueUIStyle"
] = None # The style to use when the objective is still in progress.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
is_counting_downward: t.Optional[
bool
] = None # If true, completion means having an unlock value less than or equal to the completionValue. If False, completion means having an unlock value greater than or equal to the completionValue.
location_hash: t.Optional[
int
] = None # OPTIONAL: a hash identifier for the location at which this objective must be accomplished, if there is a location defined. Look up the DestinyLocationDefinition for this hash for that additional location info.
minimum_visibility_threshold: t.Optional[
int
] = None # If nonzero, this is the minimum value at which the objective's progression should be shown. Otherwise, don't show it yet.
perks: t.Optional[
"DestinyObjectivePerkEntryDefinition"
] = None # If this objective enables Perks intrinsically, the conditions for that enabling are defined here.
progress_description: t.Optional[str] = None # Text to describe the progress bar.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
scope: t.Optional[
"DestinyGatingScope"
] = None # A shortcut for determining the most restrictive gating that this Objective is set to use. This includes both the dynamic determination of progress and of completion values. See the DestinyGatingScope enum's documentation for more details.
show_value_on_complete: t.Optional[
bool
] = None # If True, you should continue showing the progression value in the UI after it's complete. I mean, we already do that in BNet anyways, but if you want to be better behaved than us you could honor this flag.
stats: t.Optional[
"DestinyObjectiveStatEntryDefinition"
] = None # If this objective enables modifications on a player's stats intrinsically, the conditions are defined here.
value_style: t.Optional[
"DestinyUnlockValueUIStyle"
] = None # The UI style applied to the objective. It's an enum, take a look at DestinyUnlockValueUIStyle for details of the possible styles. Use this info as you wish to customize your UI.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"completionValue": to_json(self.completion_value),
"scope": to_json(self.scope),
"locationHash": to_json(self.location_hash),
"allowNegativeValue": to_json(self.allow_negative_value),
"allowValueChangeWhenCompleted": to_json(
self.allow_value_change_when_completed
),
"isCountingDownward": to_json(self.is_counting_downward),
"valueStyle": to_json(self.value_style),
"progressDescription": to_json(self.progress_description),
"perks": to_json(self.perks),
"stats": to_json(self.stats),
"minimumVisibilityThreshold": to_json(self.minimum_visibility_threshold),
"allowOvercompletion": to_json(self.allow_overcompletion),
"showValueOnComplete": to_json(self.show_value_on_complete),
"completedValueStyle": to_json(self.completed_value_style),
"inProgressValueStyle": to_json(self.in_progress_value_style),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyObjectivePerkEntryDefinition:
"""Defines the conditions under which an intrinsic perk is applied while
participating in an Objective.
These perks will generally not be benefit-granting perks, but rather
a perk that modifies gameplay in some interesting way.
"""
perk_hash: t.Optional[
int
] = None # The hash identifier of the DestinySandboxPerkDefinition that will be applied to the character.
style: t.Optional[
"DestinyObjectiveGrantStyle"
] = None # An enumeration indicating whether it will be applied as long as the Objective is active, when it's completed, or until it's completed.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"perkHash": to_json(self.perk_hash),
"style": to_json(self.style),
}
[docs]@dt.dataclass(frozen=True)
class DestinySandboxPerkDefinition:
"""Perks are modifiers to a character or item that can be applied
situationally.
- Perks determine a weapons' damage type.
- Perks put the Mods in Modifiers (they are literally the entity that bestows the Sandbox benefit for whatever fluff text about the modifier in the Socket, Plug or Talent Node)
- Perks are applied for unique alterations of state in Objectives
Anyways, I'm sure you can see why perks are so interesting.
What Perks often don't have is human readable information, so we attempt to reverse engineer that by pulling that data from places that uniquely refer to these perks: namely, Talent Nodes and Plugs. That only gives us a subset of perks that are human readable, but those perks are the ones people generally care about anyways. The others are left as a mystery, their true purpose mostly unknown and undocumented.
"""
damage_type: t.Optional[
"DamageType"
] = None # If this perk grants a damage type to a weapon, the damage type will be defined here. Unless you have a compelling reason to use this enum value, use the damageTypeHash instead to look up the actual DestinyDamageTypeDefinition.
damage_type_hash: t.Optional[
int
] = None # The hash identifier for looking up the DestinyDamageTypeDefinition, if this perk has a damage type. This is preferred over using the damageType enumeration value, which has been left purely because it is occasionally convenient.
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # These display properties are by no means guaranteed to be populated. Usually when it is, it's only because we back-filled them with the displayProperties of some Talent Node or Plug item that happened to be uniquely providing that perk.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
is_displayable: t.Optional[
bool
] = None # If true, you can actually show the perk in the UI. Otherwise, it doesn't have useful player-facing information.
perk_groups: t.Optional[
"DestinyTalentNodeStepGroups"
] = None # An old holdover from the original Armory, this was an attempt to group perks by functionality. It is as yet unpopulated, and there will be quite a bit of work needed to restore it to its former working order.
perk_identifier: t.Optional[str] = None # The string identifier for the perk.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"perkIdentifier": to_json(self.perk_identifier),
"isDisplayable": to_json(self.is_displayable),
"damageType": to_json(self.damage_type),
"damageTypeHash": to_json(self.damage_type_hash),
"perkGroups": to_json(self.perk_groups),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyTalentNodeStepGroups:
"""These properties are an attempt to categorize talent node steps by
certain common properties.
See the related enumerations for the type of properties being
categorized.
"""
damage_types: t.Optional["DestinyTalentNodeStepDamageTypes"] = None
guardian_attributes: t.Optional["DestinyTalentNodeStepGuardianAttributes"] = None
impact_effects: t.Optional["DestinyTalentNodeStepImpactEffects"] = None
light_abilities: t.Optional["DestinyTalentNodeStepLightAbilities"] = None
weapon_performance: t.Optional["DestinyTalentNodeStepWeaponPerformances"] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"weaponPerformance": to_json(self.weapon_performance),
"impactEffects": to_json(self.impact_effects),
"guardianAttributes": to_json(self.guardian_attributes),
"lightAbilities": to_json(self.light_abilities),
"damageTypes": to_json(self.damage_types),
}
[docs]class DestinyTalentNodeStepImpactEffects(Enum):
NONE = 0
ARMOR_PIERCING = 1
RICOCHET = 2
FLINCH = 4
COLLATERAL_DAMAGE = 8
DISORIENT = 16
HIGHLIGHT_TARGET = 32
ALL = 63
[docs]class DestinyTalentNodeStepGuardianAttributes(Enum):
NONE = 0
STATS = 1
SHIELDS = 2
HEALTH = 4
REVIVE = 8
AIM_UNDER_FIRE = 16
RADAR = 32
INVISIBILITY = 64
REPUTATIONS = 128
ALL = 255
[docs]class DestinyTalentNodeStepLightAbilities(Enum):
NONE = 0
GRENADES = 1
MELEE = 2
MOVEMENT_MODES = 4
ORBS = 8
SUPER_ENERGY = 16
SUPER_MODS = 32
ALL = 63
[docs]class DestinyTalentNodeStepDamageTypes(Enum):
NONE = 0
KINETIC = 1
ARC = 2
SOLAR = 4
VOID = 8
ALL = 15
[docs]@dt.dataclass(frozen=True)
class DestinyObjectiveStatEntryDefinition:
"""Defines the conditions under which stat modifications will be applied to
a Character while participating in an objective."""
stat: t.Optional[
"DestinyItemInvestmentStatDefinition"
] = None # The stat being modified, and the value used.
style: t.Optional[
"DestinyObjectiveGrantStyle"
] = None # Whether it will be applied as long as the objective is active, when it's completed, or until it's completed.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"stat": to_json(self.stat),
"style": to_json(self.style),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemInvestmentStatDefinition:
"""Represents a "raw" investment stat, before calculated stats are
calculated and before any DestinyStatGroupDefinition is applied to
transform the stat into something closer to what you see in-game.
Because these won't match what you see in-game, consider carefully
whether you really want to use these stats. I have left them in case
someone can do something useful or interesting with the pre-
processed statistics.
"""
is_conditionally_active: t.Optional[
bool
] = None # If this is true, the stat will only be applied on the item in certain game state conditions, and we can't know statically whether or not this stat will be applied. Check the "live" API data instead for whether this value is being applied on a specific instance of the item in question, and you can use this to decide whether you want to show the stat on the generic view of the item, or whether you want to show some kind of caveat or warning about the stat value being conditional on game state.
stat_type_hash: t.Optional[
int
] = None # The hash identifier for the DestinyStatDefinition defining this stat.
value: t.Optional[
int
] = None # The raw "Investment" value for the stat, before transformations are performed to turn this raw stat into stats that are displayed in the game UI.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"statTypeHash": to_json(self.stat_type_hash),
"value": to_json(self.value),
"isConditionallyActive": to_json(self.is_conditionally_active),
}
[docs]@dt.dataclass(frozen=True)
class DestinyLocationDefinition:
"""A "Location" is a sort of shortcut for referring to a specific
combination of Activity, Destination, Place, and even Bubble or NavPoint
within a space.
Most of this data isn't intrinsically useful to us, but Objectives
refer to locations, and through that we can at least infer the
Activity, Destination, and Place being referred to by the Objective.
"""
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
location_releases: t.Optional[
t.Sequence["DestinyLocationReleaseDefinition"]
] = None # A Location may refer to different specific spots in the world based on the world's current state. This is a list of those potential spots, and the data we can use at runtime to determine which one of the spots is the currently valid one.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
vendor_hash: t.Optional[
int
] = None # If the location has a Vendor on it, this is the hash identifier for that Vendor. Look them up with DestinyVendorDefinition.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"vendorHash": to_json(self.vendor_hash),
"locationReleases": to_json(self.location_releases),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyLocationReleaseDefinition:
"""A specific "spot" referred to by a location.
Only one of these can be active at a time for a given Location.
"""
activity_bubble_name: t.Optional[
int
] = None # The Activity Bubble within the Destination. Look this up in the DestinyDestinationDefinition's bubbles and bubbleSettings properties.
activity_graph_hash: t.Optional[
int
] = None # The Activity Graph being pointed to by this location.
activity_graph_node_hash: t.Optional[
int
] = None # The Activity Graph Node being pointed to by this location. (Remember that Activity Graph Node hashes are only unique within an Activity Graph: so use the combination to find the node being spoken of)
activity_hash: t.Optional[
int
] = None # The Activity being pointed to by this location.
activity_path_bundle: t.Optional[
int
] = None # If we had map information, this would tell us something cool about the path this location wants you to take. I wish we had map information.
activity_path_destination: t.Optional[
int
] = None # If we had map information, this would tell us about path information related to destination on the map. Sad. Maybe you can do something cool with it. Go to town man.
destination_hash: t.Optional[
int
] = None # The Destination being pointed to by this location.
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # Sadly, these don't appear to be populated anymore (ever?)
large_transparent_icon: t.Optional[str] = None
map_icon: t.Optional[str] = None
nav_point_type: t.Optional[
"DestinyActivityNavPointType"
] = None # The type of Nav Point that this represents. See the enumeration for more info.
small_transparent_icon: t.Optional[str] = None
spawn_point: t.Optional[
int
] = None # If we had map information, this spawnPoint would be interesting. But sadly, we don't have that info.
world_position: t.Optional[
t.Sequence[int]
] = None # Looks like it should be the position on the map, but sadly it does not look populated... yet?
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"smallTransparentIcon": to_json(self.small_transparent_icon),
"mapIcon": to_json(self.map_icon),
"largeTransparentIcon": to_json(self.large_transparent_icon),
"spawnPoint": to_json(self.spawn_point),
"destinationHash": to_json(self.destination_hash),
"activityHash": to_json(self.activity_hash),
"activityGraphHash": to_json(self.activity_graph_hash),
"activityGraphNodeHash": to_json(self.activity_graph_node_hash),
"activityBubbleName": to_json(self.activity_bubble_name),
"activityPathBundle": to_json(self.activity_path_bundle),
"activityPathDestination": to_json(self.activity_path_destination),
"navPointType": to_json(self.nav_point_type),
"worldPosition": to_json(self.world_position),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityUnlockStringDefinition:
"""Represents a status string that could be conditionally displayed about
an activity.
Note that externally, you can only see the strings themselves.
Internally we combine this information with server state to
determine which strings should be shown.
"""
display_string: t.Optional[
str
] = None # The string to be displayed if the conditions are met.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayString": to_json(self.display_string),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityPlaylistItemDefinition:
"""If the activity is a playlist, this is the definition for a specific
entry in the playlist: a single possible combination of Activity and
Activity Mode that can be chosen."""
activity_hash: t.Optional[
int
] = None # The hash identifier of the Activity that can be played. Use it to look up the DestinyActivityDefinition.
activity_mode_hashes: t.Optional[
t.Sequence[int]
] = None # The hash identifiers for Activity Modes relevant to this entry.
activity_mode_types: t.Optional[
t.Sequence["DestinyActivityModeType"]
] = None # The activity modes - if any - in enum form. Because we can't seem to escape the enums.
direct_activity_mode_hash: t.Optional[
int
] = None # If this playlist entry had an activity mode directly defined on it, this will be the hash of that mode.
direct_activity_mode_type: t.Optional[
int
] = None # If the playlist entry had an activity mode directly defined on it, this will be the enum value of that mode.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"activityHash": to_json(self.activity_hash),
"directActivityModeHash": to_json(self.direct_activity_mode_hash),
"directActivityModeType": to_json(self.direct_activity_mode_type),
"activityModeHashes": to_json(self.activity_mode_hashes),
"activityModeTypes": to_json(self.activity_mode_types),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityModeDefinition:
"""This definition represents an "Activity Mode" as it exists in the
Historical Stats endpoints. An individual Activity Mode represents a
collection of activities that are played in a certain way. For example,
Nightfall Strikes are part of a "Nightfall" activity mode, and any
activities played as the PVP mode "Clash" are part of the "Clash activity
mode.
Activity modes are nested under each other in a hierarchy, so that if you ask for - for example - "AllPvP", you will get any PVP activities that the user has played, regardless of what specific PVP mode was being played.
"""
activity_mode_category: t.Optional[
"DestinyActivityModeCategory"
] = None # The type of play being performed in broad terms (PVP, PVE)
activity_mode_mappings: t.Optional[
t.Mapping[str, "DestinyActivityModeType"]
] = None # If this exists, the mode has specific Activities (referred to by the Key) that should instead map to other Activity Modes when they are played. This was useful in D1 for Private Matches, where we wanted to have Private Matches as an activity mode while still referring to the specific mode being played.
display: t.Optional[
bool
] = None # If FALSE, we want to ignore this type when we're showing activity modes in BNet UI. It will still be returned in case 3rd parties want to use it for any purpose.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
friendly_name: t.Optional[
str
] = None # A Friendly identifier you can use for referring to this Activity Mode. We really only used this in our URLs, so... you know, take that for whatever it's worth.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
is_aggregate_mode: t.Optional[
bool
] = None # If true, this mode is an aggregation of other, more specific modes rather than being a mode in itself. This includes modes that group Features/Events rather than Gameplay, such as Trials of The Nine: Trials of the Nine being an Event that is interesting to see aggregate data for, but when you play the activities within Trials of the Nine they are more specific activity modes such as Clash.
is_team_based: t.Optional[
bool
] = None # If True, this mode has oppositional teams fighting against each other rather than "Free-For-All" or Co-operative modes of play. Note that Aggregate modes are never marked as team based, even if they happen to be team based at the moment. At any time, an aggregate whose subordinates are only team based could be changed so that one or more aren't team based, and then this boolean won't make much sense (the aggregation would become "sometimes team based"). Let's not deal with that right now.
mode_type: t.Optional[
"DestinyActivityModeType"
] = None # The Enumeration value for this Activity Mode. Pass this identifier into Stats endpoints to get aggregate stats for this mode.
order: t.Optional[int] = None # The relative ordering of activity modes.
parent_hashes: t.Optional[
t.Sequence[int]
] = None # The hash identifiers of the DestinyActivityModeDefinitions that represent all of the "parent" modes for this mode. For instance, the Nightfall Mode is also a member of AllStrikes and AllPvE.
pgcr_image: t.Optional[
str
] = None # If this activity mode has a related PGCR image, this will be the path to said image.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"pgcrImage": to_json(self.pgcr_image),
"modeType": to_json(self.mode_type),
"activityModeCategory": to_json(self.activity_mode_category),
"isTeamBased": to_json(self.is_team_based),
"isAggregateMode": to_json(self.is_aggregate_mode),
"parentHashes": to_json(self.parent_hashes),
"friendlyName": to_json(self.friendly_name),
"activityModeMappings": to_json(self.activity_mode_mappings),
"display": to_json(self.display),
"order": to_json(self.order),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityMatchmakingBlockDefinition:
"""Information about matchmaking and party size for the activity."""
is_matchmade: t.Optional[
bool
] = None # If TRUE, the activity is matchmade. Otherwise, it requires explicit forming of a party.
max_party: t.Optional[int] = None # The maximum # of people allowed in a Fireteam.
max_players: t.Optional[
int
] = None # The maximum # of people allowed across all teams in the activity.
min_party: t.Optional[
int
] = None # The minimum # of people in the fireteam for the activity to launch.
requires_guardian_oath: t.Optional[
bool
] = None # If true, you have to Solemnly Swear to be up to Nothing But Good(tm) to play.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"isMatchmade": to_json(self.is_matchmade),
"minParty": to_json(self.min_party),
"maxParty": to_json(self.max_party),
"maxPlayers": to_json(self.max_players),
"requiresGuardianOath": to_json(self.requires_guardian_oath),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityGuidedBlockDefinition:
"""Guided Game information for this activity."""
guided_disband_count: t.Optional[
int
] = None # If -1, the guided group cannot be disbanded. Otherwise, take the total # of players in the activity and subtract this number: that is the total # of votes needed for the guided group to disband.
guided_max_lobby_size: t.Optional[
int
] = None # The maximum amount of people that can be in the waiting lobby.
guided_min_lobby_size: t.Optional[
int
] = None # The minimum amount of people that can be in the waiting lobby.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"guidedMaxLobbySize": to_json(self.guided_max_lobby_size),
"guidedMinLobbySize": to_json(self.guided_min_lobby_size),
"guidedDisbandCount": to_json(self.guided_disband_count),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityLoadoutRequirementSet:
requirements: t.Optional[
t.Sequence["DestinyActivityLoadoutRequirement"]
] = None # The set of requirements that will be applied on the activity if this requirement set is active.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"requirements": to_json(self.requirements),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityLoadoutRequirement:
allowed_equipped_item_hashes: t.Optional[t.Sequence[int]] = None
allowed_weapon_sub_types: t.Optional[t.Sequence["DestinyItemSubType"]] = None
equipment_slot_hash: t.Optional[int] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"equipmentSlotHash": to_json(self.equipment_slot_hash),
"allowedEquippedItemHashes": to_json(self.allowed_equipped_item_hashes),
"allowedWeaponSubTypes": to_json(self.allowed_weapon_sub_types),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityInsertionPointDefinition:
"""A point of entry into an activity, gated by an unlock flag and with some
more-or-less useless (for our purposes) phase information.
I'm including it in case we end up being able to bolt more useful information onto it in the future.
UPDATE: Turns out this information isn't actually useless, and is in fact actually useful for people. Who would have thought? We still don't have localized info for it, but at least this will help people when they're looking at phase indexes in stats data, or when they want to know what phases have been completed on a weekly achievement.
"""
phase_hash: t.Optional[
int
] = None # A unique hash value representing the phase. This can be useful for, for example, comparing how different instances of Raids have phases in different orders!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"phaseHash": to_json(self.phase_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyPlaceDefinition:
"""Okay, so Activities (DestinyActivityDefinition) take place in
Destinations (DestinyDestinationDefinition).
Destinations are part of larger locations known as Places (you're
reading its documentation right now). Places are more on the
planetary scale, like "Earth" and "Your Mom."
"""
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyActivityTypeDefinition:
"""The definition for an Activity Type.
In Destiny 2, an Activity Type represents a conceptual
categorization of Activities. These are most commonly used in the
game for the subtitle under Activities, but BNet uses them
extensively to identify and group activities by their common
properties. Unfortunately, there has been a movement away from
providing the richer data in Destiny 2 that we used to get in
Destiny 1 for Activity Types. For instance, Nightfalls are grouped
under the same Activity Type as regular Strikes. For this reason,
BNet will eventually migrate toward Activity Modes as a better
indicator of activity category. But for the time being, it is still
referred to in many places across our codebase.
"""
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyUnlockExpressionDefinition:
"""Where the sausage gets made.
Unlock Expressions are the foundation of the game's gating mechanics
and investment-related restrictions. They can test Unlock Flags and
Unlock Values for certain states, using a sufficient amount of
logical operators such that unlock expressions are effectively
Turing complete. Use UnlockExpressionParser to evaluate expressions
using an IUnlockContext parsed from Babel.
"""
scope: t.Optional[
"DestinyGatingScope"
] = None # A shortcut for determining the most restrictive gating that this expression performs. See the DestinyGatingScope enum's documentation for more details.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"scope": to_json(self.scope),
}
[docs]@dt.dataclass(frozen=True)
class DestinyDestinationBubbleSettingDefinition:
"""Human readable data about the bubble. Combine with DestinyBubbleDefinition - see DestinyDestinationDefinition.bubbleSettings for more information.
DEPRECATED - Just use bubbles."""
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
}
[docs]@dt.dataclass(frozen=True)
class DestinyBubbleDefinition:
"""Basic identifying data about the bubble.
Combine with DestinyDestinationBubbleSettingDefinition - see DestinyDestinationDefinition.bubbleSettings for more information.
"""
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The display properties of this bubble, so you don't have to look them up in a separate list anymore.
hash: t.Optional[
int
] = None # The identifier for the bubble: only guaranteed to be unique within the Destination.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"hash": to_json(self.hash),
"displayProperties": to_json(self.display_properties),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorGroupReference:
vendor_group_hash: t.Optional[
int
] = None # The DestinyVendorGroupDefinition to which this Vendor can belong.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"vendorGroupHash": to_json(self.vendor_group_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyVendorGroupDefinition:
"""BNet attempts to group vendors into similar collections.
These groups aren't technically game canonical, but they are helpful
for filtering vendors or showing them organized into a clean view on
a webpage or app. These definitions represent the groups we've
built. Unlike in Destiny 1, a Vendors' group may change dynamically
as the game state changes: thus, you will want to check
DestinyVendorComponent responses to find a vendor's currently active
Group (if you care). Using this will let you group your vendors in
your UI in a similar manner to how we will do grouping in the
Companion.
"""
category_name: t.Optional[str] = None # For now, a group just has a name.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
order: t.Optional[
int
] = None # The recommended order in which to render the groups, Ascending order.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"order": to_json(self.order),
"categoryName": to_json(self.category_name),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyFactionDefinition:
"""These definitions represent Factions in the game.
Factions have ended up unilaterally being related to Vendors that
represent them, but that need not necessarily be the case. A Faction
is really just an entity that has a related progression for which a
character can gain experience. In Destiny 1, Dead Orbit was an
example of a Faction: there happens to be a Vendor that represents
Dead Orbit (and indeed, DestinyVendorDefinition.factionHash defines
to this relationship), but Dead Orbit could theoretically exist
without the Vendor that provides rewards.
"""
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
progression_hash: t.Optional[
int
] = None # The hash identifier for the DestinyProgressionDefinition that indicates the character's relationship with this faction in terms of experience and levels.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
reward_item_hash: t.Optional[
int
] = None # The faction reward item hash, usually an engram.
reward_vendor_hash: t.Optional[
int
] = None # The faction reward vendor hash, used for faction engram previews.
token_values: t.Optional[
t.Mapping[str, int]
] = None # The faction token item hashes, and their respective progression values.
vendors: t.Optional[
t.Sequence["DestinyFactionVendorDefinition"]
] = None # List of vendors that are associated with this faction. The last vendor that passes the unlock flag checks is the one that should be shown.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"progressionHash": to_json(self.progression_hash),
"tokenValues": to_json(self.token_values),
"rewardItemHash": to_json(self.reward_item_hash),
"rewardVendorHash": to_json(self.reward_vendor_hash),
"vendors": to_json(self.vendors),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyFactionVendorDefinition:
"""These definitions represent faction vendors at different points in the
game.
A single faction may contain multiple vendors, or the same vendor
available at two different locations.
"""
background_image_path: t.Optional[
str
] = None # The relative path to the background image representing this Vendor at this location, for use in a banner.
destination_hash: t.Optional[
int
] = None # The hash identifier for a Destination at which this vendor may be located. Each destination where a Vendor may exist will only ever have a single entry.
vendor_hash: t.Optional[int] = None # The faction vendor hash.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"vendorHash": to_json(self.vendor_hash),
"destinationHash": to_json(self.destination_hash),
"backgroundImagePath": to_json(self.background_image_path),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemQualityBlockDefinition:
"""An item's "Quality" determines its calculated stats.
The Level at which the item spawns is combined with its
"qualityLevel" along with some additional calculations to determine
the value of those stats. In Destiny 2, most items don't have
default item levels and quality, making this property less useful:
these apparently are almost always determined by the complex
mechanisms of the Reward system rather than statically. They are
still provided here in case they are still useful for people. This
also contains some information about Infusion.
"""
current_version: t.Optional[
int
] = None # The latest version available for this item.
display_version_watermark_icons: t.Optional[
t.Sequence[str]
] = None # Icon overlays to denote the item version and power cap status.
infusion_category_hash: t.Optional[
int
] = None # The hash identifier for the infusion. It does not map to a Definition entity. DEPRECATED: Items can now have multiple infusion categories. Please use infusionCategoryHashes instead.
infusion_category_hashes: t.Optional[
t.Sequence[int]
] = None # If any one of these hashes matches any value in another item's infusionCategoryHashes, the two can infuse with each other.
infusion_category_name: t.Optional[
str
] = None # The string identifier for this item's "infusability", if any. Items that match the same infusionCategoryName are allowed to infuse with each other. DEPRECATED: Items can now have multiple infusion categories. Please use infusionCategoryHashes instead.
item_levels: t.Optional[
t.Sequence[int]
] = None # The "base" defined level of an item. This is a list because, in theory, each Expansion could define its own base level for an item. In practice, not only was that never done in Destiny 1, but now this isn't even populated at all. When it's not populated, the level at which it spawns has to be inferred by Reward information, of which BNet receives an imperfect view and will only be reliable on instanced data as a result.
progression_level_requirement_hash: t.Optional[
int
] = None # An item can refer to pre-set level requirements. They are defined in DestinyProgressionLevelRequirementDefinition, and you can use this hash to find the appropriate definition.
quality_level: t.Optional[
int
] = None # qualityLevel is used in combination with the item's level to calculate stats like Attack and Defense. It plays a role in that calculation, but not nearly as large as itemLevel does.
versions: t.Optional[
t.Sequence["DestinyItemVersionDefinition"]
] = None # The list of versions available for this item.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"itemLevels": to_json(self.item_levels),
"qualityLevel": to_json(self.quality_level),
"infusionCategoryName": to_json(self.infusion_category_name),
"infusionCategoryHash": to_json(self.infusion_category_hash),
"infusionCategoryHashes": to_json(self.infusion_category_hashes),
"progressionLevelRequirementHash": to_json(
self.progression_level_requirement_hash
),
"currentVersion": to_json(self.current_version),
"versions": to_json(self.versions),
"displayVersionWatermarkIcons": to_json(
self.display_version_watermark_icons
),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemVersionDefinition:
"""The version definition currently just holds a reference to the power
cap."""
power_cap_hash: t.Optional[
int
] = None # A reference to the power cap for this item version.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"powerCapHash": to_json(self.power_cap_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemValueBlockDefinition:
"""This defines an item's "Value".
Unfortunately, this appears to be used in different ways depending
on the way that the item itself is used. For items being sold at a
Vendor, this is the default "sale price" of the item. These days,
the vendor itself almost always sets the price, but it still
possible for the price to fall back to this value. For quests, it is
a preview of rewards you can gain by completing the quest. For dummy
items, if the itemValue refers to an Emblem, it is the emblem that
should be shown as the reward. (jeez louise) It will likely be used
in a number of other ways in the future, it appears to be a bucket
where they put arbitrary items and quantities into the item.
"""
item_value: t.Optional[
t.Sequence["DestinyItemQuantity"]
] = None # References to the items that make up this item's "value", and the quantity.
value_description: t.Optional[
str
] = None # If there's a localized text description of the value provided, this will be said description.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"itemValue": to_json(self.item_value),
"valueDescription": to_json(self.value_description),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSourceBlockDefinition:
"""Data about an item's "sources": ways that the item can be obtained."""
exclusive: t.Optional[
"BungieMembershipType"
] = None # If we found that this item is exclusive to a specific platform, this will be set to the BungieMembershipType enumeration that matches that platform.
source_hashes: t.Optional[
t.Sequence[int]
] = None # The list of hash identifiers for Reward Sources that hint where the item can be found (DestinyRewardSourceDefinition).
sources: t.Optional[
t.Sequence["DestinyItemSourceDefinition"]
] = None # A collection of details about the stats that were computed for the ways we found that the item could be spawned.
vendor_sources: t.Optional[
t.Sequence["DestinyItemVendorSourceReference"]
] = None # A denormalized reference back to vendors that potentially sell this item.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"sourceHashes": to_json(self.source_hashes),
"sources": to_json(self.sources),
"exclusive": to_json(self.exclusive),
"vendorSources": to_json(self.vendor_sources),
}
[docs]@dt.dataclass(frozen=True)
class DestinyRewardSourceDefinition:
"""Represents a heuristically-determined "item source" according to
Bungie.net.
These item sources are non-canonical: we apply a combination of
special configuration and often-fragile heuristics to attempt to
discern whether an item should be part of a given "source," but we
have known cases of false positives and negatives due to our
imperfect heuristics. Still, they provide a decent approximation for
people trying to figure out how an item can be obtained.
DestinyInventoryItemDefinition refers to sources in the
sourceDatas.sourceHashes property for all sources we determined the
item could spawn from. An example in Destiny 1 of a Source would be
"Nightfall". If an item has the "Nightfall" source associated with
it, it's extremely likely that you can earn that item while playing
Nightfall, either during play or as an after-completion reward.
"""
category: t.Optional[
"DestinyRewardSourceCategory"
] = None # Sources are grouped into categories: common ways that items are provided. I hope to see this expand in Destiny 2 once we have time to generate accurate reward source data.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"category": to_json(self.category),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]class DestinyRewardSourceCategory(Enum):
"""BNet's custom categorization of reward sources.
We took a look at the existing ways that items could be spawned, and
tried to make high-level categorizations of them. This needs to be
re-evaluated for Destiny 2.
"""
NONE = 0 # The source doesn't fit well into any of the other types.
ACTIVITY = 1 # The source is directly related to the rewards gained by playing an activity or set of activities. This currently includes Quests and other action in-game.
VENDOR = 2 # This source is directly related to items that Vendors sell.
AGGREGATE = 3 # This source is a custom aggregation of items that can be earned in many ways, but that share some other property in common that is useful to share. For instance, in Destiny 1 we would make "Reward Sources" for every game expansion: that way, you could search reward sources to see what items became available with any given Expansion.
[docs]@dt.dataclass(frozen=True)
class DestinyItemVendorSourceReference:
"""Represents that a vendor could sell this item, and provides a quick link
to that vendor and sale item.
Note that we do not and cannot make a guarantee that the vendor will
ever *actually* sell this item, only that the Vendor has a
definition that indicates it *could* be sold. Note also that a
vendor may sell the same item in multiple "ways", which means there
may be multiple vendorItemIndexes for a single Vendor hash.
"""
vendor_hash: t.Optional[
int
] = None # The identifier for the vendor that may sell this item.
vendor_item_indexes: t.Optional[
t.Sequence[int]
] = None # The Vendor sale item indexes that represent the sale information for this item. The same vendor may sell an item in multiple "ways", hence why this is a list. (for instance, a weapon may be "sold" as a reward in a quest, for Glimmer, and for Masterwork Cores: each of those ways would be represented by a different vendor sale item with a different index)
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"vendorHash": to_json(self.vendor_hash),
"vendorItemIndexes": to_json(self.vendor_item_indexes),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemObjectiveBlockDefinition:
"""An item can have objectives on it.
In practice, these are the exclusive purview of "Quest Step" items:
DestinyInventoryItemDefinitions that represent a specific step in a
Quest. Quest steps have 1:M objectives that we end up processing and
returning in live data as DestinyQuestStatus data, and other useful
information.
"""
display_activity_hashes: t.Optional[
t.Sequence[int]
] = None # For every entry in objectiveHashes, there is a corresponding entry in this array at the same index. If the objective is meant to be associated with a specific DestinyActivityDefinition, there will be a valid hash at that index. Otherwise, it will be invalid (0). Rendered somewhat obsolete by perObjectiveDisplayProperties, which currently has much the same information but may end up with more info in the future.
display_as_stat_tracker: t.Optional[bool] = None
narrative: t.Optional[
str
] = None # The localized string for narrative text related to this quest step, if any.
objective_hashes: t.Optional[
t.Sequence[int]
] = None # The hashes to Objectives (DestinyObjectiveDefinition) that are part of this Quest Step, in the order that they should be rendered.
objective_verb_name: t.Optional[
str
] = None # The localized string describing an action to be performed associated with the objectives, if any.
per_objective_display_properties: t.Optional[
t.Sequence["DestinyObjectiveDisplayProperties"]
] = None # One entry per Objective on the item, it will have related display information.
quest_type_hash: t.Optional[
int
] = None # A hashed value for the questTypeIdentifier, because apparently I like to be redundant.
quest_type_identifier: t.Optional[
str
] = None # The identifier for the type of quest being performed, if any. Not associated with any fixed definition, yet.
questline_item_hash: t.Optional[
int
] = None # The hash for the DestinyInventoryItemDefinition representing the Quest to which this Quest Step belongs.
require_full_objective_completion: t.Optional[
bool
] = None # If True, all objectives must be completed for the step to be completed. If False, any one objective can be completed for the step to be completed.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"objectiveHashes": to_json(self.objective_hashes),
"displayActivityHashes": to_json(self.display_activity_hashes),
"requireFullObjectiveCompletion": to_json(
self.require_full_objective_completion
),
"questlineItemHash": to_json(self.questline_item_hash),
"narrative": to_json(self.narrative),
"objectiveVerbName": to_json(self.objective_verb_name),
"questTypeIdentifier": to_json(self.quest_type_identifier),
"questTypeHash": to_json(self.quest_type_hash),
"perObjectiveDisplayProperties": to_json(
self.per_objective_display_properties
),
"displayAsStatTracker": to_json(self.display_as_stat_tracker),
}
[docs]@dt.dataclass(frozen=True)
class DestinyObjectiveDisplayProperties:
activity_hash: t.Optional[
int
] = None # The activity associated with this objective in the context of this item, if any.
display_on_item_preview_screen: t.Optional[
bool
] = None # If true, the game shows this objective on item preview screens.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"activityHash": to_json(self.activity_hash),
"displayOnItemPreviewScreen": to_json(self.display_on_item_preview_screen),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemMetricBlockDefinition:
"""The metrics available for display and selection on an item."""
available_metric_category_node_hashes: t.Optional[
t.Sequence[int]
] = None # Hash identifiers for any DestinyPresentationNodeDefinition entry that can be used to list available metrics. Any metric listed directly below these nodes, or in any of these nodes' children will be made available for selection.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"availableMetricCategoryNodeHashes": to_json(
self.available_metric_category_node_hashes
),
}
[docs]@dt.dataclass(frozen=True)
class DestinyMaterialRequirementSetDefinition:
"""Represent a set of material requirements: Items that either need to be
owned or need to be consumed in order to perform an action.
A variety of other entities refer to these as gatekeepers and
payments for actions that can be performed in game.
"""
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
materials: t.Optional[
t.Sequence["DestinyMaterialRequirement"]
] = None # The list of all materials that are required.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"materials": to_json(self.materials),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyMaterialRequirement:
"""Many actions relating to items require you to expend materials: - Activating a talent node - Inserting a plug into a socket The items will refer to material requirements by a materialRequirementsHash in these cases, and this is the definition for those requirements in terms of the item required, how much of it is required and other interesting info. This is one of the rare/strange times where a single contract class is used both in definitions *and* in live data response contracts. I'm not sure yet whether I regret that."""
count: t.Optional[int] = None # The amount of the material required.
delete_on_action: t.Optional[
bool
] = None # If True, the material will be removed from the character's inventory when the action is performed.
item_hash: t.Optional[
int
] = None # The hash identifier of the material required. Use it to look up the material's DestinyInventoryItemDefinition.
omit_from_requirements: t.Optional[
bool
] = None # If True, this requirement is "silent": don't bother showing it in a material requirements display. I mean, I'm not your mom: I'm not going to tell you you *can't* show it. But we won't show it in our UI.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"itemHash": to_json(self.item_hash),
"deleteOnAction": to_json(self.delete_on_action),
"count": to_json(self.count),
"omitFromRequirements": to_json(self.omit_from_requirements),
}
[docs]@dt.dataclass(frozen=True)
class DestinyUnlockValueDefinition:
"""An Unlock Value is an internal integer value, stored on the server and
used in a variety of ways, most frequently for the gating/requirement
checks that the game performs across all of its main features.
They can also be used as the storage data for mapped Progressions,
Objectives, and other features that require storage of variable
numeric values.
"""
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyGenderDefinition:
"""Gender is a social construct, and as such we have definitions for
Genders.
Right now there happens to only be two, but we'll see what the
future holds.
"""
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
gender_type: t.Optional[
"DestinyGender"
] = None # This is a quick reference enumeration for all of the currently defined Genders. We use the enumeration for quicker lookups in related data, like DestinyClassDefinition.genderedClassNames.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"genderType": to_json(self.gender_type),
"displayProperties": to_json(self.display_properties),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemGearsetBlockDefinition:
"""If an item has a related gearset, this is the list of items in that set,
and an unlock expression that evaluates to a number representing the
progress toward gearset completion (a very rare use for unlock
expressions!)"""
item_list: t.Optional[
t.Sequence[int]
] = None # The list of hashes for items in the gearset. Use them to look up DestinyInventoryItemDefinition entries for the items in the set.
tracking_value_max: t.Optional[
int
] = None # The maximum possible number of items that can be collected.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"trackingValueMax": to_json(self.tracking_value_max),
"itemList": to_json(self.item_list),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSackBlockDefinition:
"""Some items are "sacks" - they can be "opened" to produce other items. This is information related to its sack status, mostly UI strings. Engrams are an example of items that are considered to be "Sacks"."""
detail_action: t.Optional[
str
] = None # A description of what will happen when you open the sack. As far as I can tell, this is blank currently. Unknown whether it will eventually be populated with useful info.
open_action: t.Optional[
str
] = None # The localized name of the action being performed when you open the sack.
open_on_acquire: t.Optional[bool] = None
select_item_count: t.Optional[int] = None
vendor_sack_type: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"detailAction": to_json(self.detail_action),
"openAction": to_json(self.open_action),
"selectItemCount": to_json(self.select_item_count),
"vendorSackType": to_json(self.vendor_sack_type),
"openOnAcquire": to_json(self.open_on_acquire),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSocketBlockDefinition:
"""If defined, the item has at least one socket."""
detail: t.Optional[
str
] = None # This was supposed to be a string that would give per-item details about sockets. In practice, it turns out that all this ever has is the localized word "details". ... that's lame, but perhaps it will become something cool in the future.
intrinsic_sockets: t.Optional[
t.Sequence["DestinyItemIntrinsicSocketEntryDefinition"]
] = None # Each intrinsic (or immutable/permanent) socket on an item is defined here, along with the plug that is permanently affixed to the socket.
socket_categories: t.Optional[
t.Sequence["DestinyItemSocketCategoryDefinition"]
] = None # A convenience property, that refers to the sockets in the "sockets" property, pre-grouped by category and ordered in the manner that they should be grouped in the UI. You could form this yourself with the existing data, but why would you want to? Enjoy life man.
socket_entries: t.Optional[
t.Sequence["DestinyItemSocketEntryDefinition"]
] = None # Each non-intrinsic (or mutable) socket on an item is defined here. Check inside for more info.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"detail": to_json(self.detail),
"socketEntries": to_json(self.socket_entries),
"intrinsicSockets": to_json(self.intrinsic_sockets),
"socketCategories": to_json(self.socket_categories),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSocketEntryDefinition:
"""The definition information for a specific socket on an item.
This will determine how the socket behaves in-game.
"""
default_visible: t.Optional[
bool
] = None # If true, then this socket is visible in the item's "default" state. If you have an instance, you should always check the runtime state, as that can override this visibility setting: but if you're looking at the item on a conceptual level, this property can be useful for hiding data such as legacy sockets - which remain defined on items for infrastructure purposes, but can be confusing for users to see.
hide_perks_in_item_tooltip: t.Optional[
bool
] = None # If this is true, the perks provided by this socket shouldn't be shown in the item's tooltip. This might be useful if it's providing a hidden bonus, or if the bonus is less important than other benefits on the item.
plug_sources: t.Optional[
"SocketPlugSources"
] = None # Indicates where you should go to get plugs for this socket. This will affect how you populate your UI, as well as what plugs are valid for this socket. It's an alternative to having to check for the existence of certain properties (reusablePlugItems for example) to infer where plugs should come from.
prevent_initialization_on_vendor_purchase: t.Optional[
bool
] = None # If this is true, then the socket will not be initialized with a plug if the item is purchased from a Vendor. Remember that Vendors are much more than conceptual vendors: they include "Collection Kiosks" and other entities. See DestinyVendorDefinition for more information.
randomized_plug_set_hash: t.Optional[
int
] = None # This field replaces "randomizedPlugItems" as of Shadowkeep launch. If a socket has randomized plugs, this is a pointer to the set of plugs that could be used, as defined in DestinyPlugSetDefinition. If null, the item has no randomized plugs.
reusable_plug_items: t.Optional[
t.Sequence["DestinyItemSocketEntryPlugItemDefinition"]
] = None # This is a list of pre-determined plugs that can *always* be plugged into this socket, without the character having the plug in their inventory. If this list is populated, you will not be allowed to plug an arbitrary item in the socket: you will only be able to choose from one of these reusable plugs.
reusable_plug_set_hash: t.Optional[
int
] = None # If this socket's plugs come from a reusable DestinyPlugSetDefinition, this is the identifier for that set. We added this concept to reduce some major duplication that's going to come from sockets as replacements for what was once implemented as large sets of items and kiosks (like Emotes). As of Shadowkeep, these will come up much more frequently and be driven by game content rather than custom curation.
single_initial_item_hash: t.Optional[
int
] = None # If a valid hash, this is the hash identifier for the DestinyInventoryItemDefinition representing the Plug that will be initially inserted into the item on item creation. Otherwise, this Socket will either start without a plug inserted, or will have one randomly inserted.
socket_type_hash: t.Optional[
int
] = None # All sockets have a type, and this is the hash identifier for this particular type. Use it to look up the DestinySocketTypeDefinition: read there for more information on how socket types affect the behavior of the socket.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"socketTypeHash": to_json(self.socket_type_hash),
"singleInitialItemHash": to_json(self.single_initial_item_hash),
"reusablePlugItems": to_json(self.reusable_plug_items),
"preventInitializationOnVendorPurchase": to_json(
self.prevent_initialization_on_vendor_purchase
),
"hidePerksInItemTooltip": to_json(self.hide_perks_in_item_tooltip),
"plugSources": to_json(self.plug_sources),
"reusablePlugSetHash": to_json(self.reusable_plug_set_hash),
"randomizedPlugSetHash": to_json(self.randomized_plug_set_hash),
"defaultVisible": to_json(self.default_visible),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSocketEntryPlugItemDefinition:
"""The definition of a known, reusable plug that can be applied to a
socket."""
plug_item_hash: t.Optional[
int
] = None # The hash identifier of a DestinyInventoryItemDefinition representing the plug that can be inserted.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"plugItemHash": to_json(self.plug_item_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSocketEntryPlugItemRandomizedDefinition:
currently_can_roll: t.Optional[
bool
] = None # Indicates if the plug can be rolled on the current version of the item. For example, older versions of weapons may have plug rolls that are no longer possible on the current versions.
plug_item_hash: t.Optional[
int
] = None # The hash identifier of a DestinyInventoryItemDefinition representing the plug that can be inserted.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"currentlyCanRoll": to_json(self.currently_can_roll),
"plugItemHash": to_json(self.plug_item_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemIntrinsicSocketEntryDefinition:
"""Represents a socket that has a plug associated with it intrinsically.
This is useful for situations where the weapon needs to have a
visual plug/Mod on it, but that plug/Mod should never change.
"""
default_visible: t.Optional[
bool
] = None # If true, then this socket is visible in the item's "default" state. If you have an instance, you should always check the runtime state, as that can override this visibility setting: but if you're looking at the item on a conceptual level, this property can be useful for hiding data such as legacy sockets - which remain defined on items for infrastructure purposes, but can be confusing for users to see.
plug_item_hash: t.Optional[
int
] = None # Indicates the plug that is intrinsically inserted into this socket.
socket_type_hash: t.Optional[
int
] = None # Indicates the type of this intrinsic socket.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"plugItemHash": to_json(self.plug_item_hash),
"socketTypeHash": to_json(self.socket_type_hash),
"defaultVisible": to_json(self.default_visible),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSocketCategoryDefinition:
"""Sockets are grouped into categories in the UI.
These define which category and which sockets are under that
category.
"""
socket_category_hash: t.Optional[
int
] = None # The hash for the Socket Category: a quick way to go get the header display information for the category. Use it to look up DestinySocketCategoryDefinition info.
socket_indexes: t.Optional[
t.Sequence[int]
] = None # Use these indexes to look up the sockets in the "sockets.socketEntries" property on the item definition. These are the indexes under the category, in game-rendered order.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"socketCategoryHash": to_json(self.socket_category_hash),
"socketIndexes": to_json(self.socket_indexes),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemSummaryBlockDefinition:
"""This appears to be information used when rendering rewards.
We don't currently use it on BNet.
"""
sort_priority: t.Optional[
int
] = None # Apparently when rendering an item in a reward, this should be used as a sort priority. We're not doing it presently.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"sortPriority": to_json(self.sort_priority),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemTalentGridBlockDefinition:
"""This defines information that can only come from a talent grid on an
item.
Items mostly have negligible talent grid data these days, but
instanced items still retain grids as a source for some of this
common information. Builds/Subclasses are the only items left that
still have talent grids with meaningful Nodes.
"""
build_name: t.Optional[
str
] = None # A shortcut string identifier for the "build" in question, if this talent grid has an associated build. Doesn't map to anything we can expose at the moment.
hud_damage_type: t.Optional[
"DamageType"
] = None # If the talent grid implies a damage type, this is the enum value for that damage type.
hud_icon: t.Optional[
str
] = None # If the talent grid has a special icon that's shown in the game UI (like builds, funny that), this is the identifier for that icon. Sadly, we don't actually get that icon right now. I'll be looking to replace this with a path to the actual icon itself.
item_detail_string: t.Optional[
str
] = None # This is meant to be a subtitle for looking at the talent grid. In practice, somewhat frustratingly, this always merely says the localized word for "Details". Great. Maybe it'll have more if talent grids ever get used for more than builds and subclasses again.
talent_grid_hash: t.Optional[
int
] = None # The hash identifier of the DestinyTalentGridDefinition attached to this item.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"talentGridHash": to_json(self.talent_grid_hash),
"itemDetailString": to_json(self.item_detail_string),
"buildName": to_json(self.build_name),
"hudDamageType": to_json(self.hud_damage_type),
"hudIcon": to_json(self.hud_icon),
}
[docs]@dt.dataclass(frozen=True)
class DestinyTalentGridDefinition:
"""The time has unfortunately come to talk about Talent Grids. Talent Grids
are the most complex and unintuitive part of the Destiny Definition data.
Grab a cup of coffee before we begin, I can wait. Talent Grids were the
primary way that items could be customized in Destiny 1. In Destiny 2, for
now, talent grids have become exclusively used by Subclass/Build items: but
the system is still in place for it to be used by items should the
direction change back toward talent grids. Talent Grids have Nodes: the
visual circles on the talent grid detail screen that have icons and can be
activated if you meet certain requirements and pay costs. The actual visual
data and effects, however, are driven by the "Steps" on Talent Nodes. Any
given node will have 1:M of these steps, and the specific step that will be
considered the "current" step (and thus the dictator of all benefits,
visual state, and activation requirements on the Node) will almost always
not be determined until an instance of the item is created. This is how, in
Destiny 1, items were able to have such a wide variety of what users saw as
"Perks": they were actually Talent Grids with nodes that had a wide variety
of Steps, randomly chosen at the time of item creation. Now that Talent
Grids are used exclusively by subclasses and builds, all of the properties
within still apply: but there are additional visual elements on the
Subclass/Build screens that are superimposed on top of the talent nodes.
Unfortunately, BNet doesn't have this data: if you want to build a subclass
screen, you will have to provide your own "decorative" assets, such as the
visual connectors between nodes and the fancy colored-fire-bathed character
standing behind the nodes.
DestinyInventoryItem.talentGrid.talentGridHash defines an item's linked Talent Grid, which brings you to this definition that contains enough satic data about talent grids to make your head spin. These *must* be combined with instanced data - found when live data returns DestinyItemTalentGridComponent - in order to derive meaning. The instanced data will reference nodes and steps within these definitions, which you will then have to look up in the definition and combine with the instanced data to give the user the visual representation of their item's talent grid.
"""
exclusive_sets: t.Optional[
t.Sequence["DestinyTalentNodeExclusiveSetDefinition"]
] = None # Talent Nodes can exist in "exclusive sets": these are sets of nodes in which only a single node in the set can be activated at any given time. Activating a node in this set will automatically deactivate the other nodes in the set (referred to as a "Swap"). If a node in the exclusive set has already been activated, the game will not charge you materials to activate another node in the set, even if you have never activated it before, because you already paid the cost to activate one node in the set. Not to be confused with Exclusive Groups. (how the heck do we NOT get confused by that? Jeez) See the groups property for information about that only-tangentially-related concept.
grid_level_per_column: t.Optional[
int
] = None # The meaning of this has been lost in the sands of time: it still exists as a property, but appears to be unused in the modern UI of talent grids. It used to imply that each visual "column" of talent nodes required identical progression levels in order to be activated. Returning this value in case it is still useful to someone? Perhaps it's just a bit of interesting history.
groups: t.Optional[
t.Mapping[str, "DestinyTalentExclusiveGroup"]
] = None # Talent Nodes can have "Exclusive Groups". These are not to be confused with Exclusive Sets (see exclusiveSets property). Look at the definition of DestinyTalentExclusiveGroup for more information and how they work. These groups are keyed by the "groupHash" from DestinyTalentExclusiveGroup.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
independent_node_indexes: t.Optional[
t.Sequence[int]
] = None # This is a quick reference to the indexes of nodes that are not part of exclusive sets. Handy for knowing which talent nodes can only be activated directly, rather than via swapping.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
max_grid_level: t.Optional[
int
] = None # The maximum possible level of the Talent Grid: at this level, any nodes are allowed to be activated.
node_categories: t.Optional[
t.Sequence["DestinyTalentNodeCategory"]
] = None # BNet wants to show talent nodes grouped by similar purpose with localized titles. This is the ordered list of those categories: if you want to show nodes by category, you can iterate over this list, render the displayProperties for the category as the title, and then iterate over the talent nodes referenced by the category to show the related nodes. Note that this is different from Exclusive Groups or Sets, because these categories also incorporate "Independent" nodes that belong to neither sets nor groups. These are purely for visual grouping of nodes rather than functional grouping.
nodes: t.Optional[
t.Sequence["DestinyTalentNodeDefinition"]
] = None # The list of Talent Nodes on the Grid (recall that Nodes themselves are really just locations in the UI to show whatever their current Step is. You will only know the current step for a node by retrieving instanced data through platform calls to the API that return DestinyItemTalentGridComponent).
progression_hash: t.Optional[
int
] = None # The hash identifier of the Progression (DestinyProgressionDefinition) that drives whether and when Talent Nodes can be activated on the Grid. Items will have instances of this Progression, and will gain experience that will eventually cause the grid to increase in level. As the grid's level increases, it will cross the threshold where nodes can be activated. See DestinyTalentGridStepDefinition's activation requirements for more information.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"maxGridLevel": to_json(self.max_grid_level),
"gridLevelPerColumn": to_json(self.grid_level_per_column),
"progressionHash": to_json(self.progression_hash),
"nodes": to_json(self.nodes),
"exclusiveSets": to_json(self.exclusive_sets),
"independentNodeIndexes": to_json(self.independent_node_indexes),
"groups": to_json(self.groups),
"nodeCategories": to_json(self.node_categories),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyTalentNodeDefinition:
"""Talent Grids on items have Nodes.
These nodes have positions in the talent grid's UI, and contain
"Steps" (DestinyTalentNodeStepDefinition), one of whom will be the
"Current" step. The Current Step determines the visual properties of
the node, as well as what the node grants when it is activated. See
DestinyTalentGridDefinition for a more complete overview of how
Talent Grids work, and how they are used in Destiny 2 (and how they
were used in Destiny 1).
"""
auto_unlocks: t.Optional[
bool
] = None # If true, this node will automatically unlock when the Talent Grid's level reaches the required level of the current step of this node.
binary_pair_node_index: t.Optional[
int
] = None # At one point, Talent Nodes supported the idea of "Binary Pairs": nodes that overlapped each other visually, and where activating one deactivated the other. They ended up not being used, mostly because Exclusive Sets are *almost* a superset of this concept, but the potential for it to be used still exists in theory. If this is ever used, this will be the index into the DestinyTalentGridDefinition.nodes property for the node that is the binary pair match to this node. Activating one deactivates the other.
column: t.Optional[
int
] = None # The visual "column" where the node should be shown in the UI. If negative, the node is hidden.
exclusive_with_node_hashes: t.Optional[
t.Sequence[int]
] = None # The nodeHash values for nodes that are in an Exclusive Set with this node. See DestinyTalentGridDefinition.exclusiveSets for more info about exclusive sets. Again, note that these are nodeHashes and *not* nodeIndexes.
group_hash: t.Optional[
int
] = None # As of Destiny 2, nodes can exist as part of "Exclusive Groups". These differ from exclusive sets in that, within the group, many nodes can be activated. But the act of activating any node in the group will cause "opposing" nodes (nodes in groups that are not allowed to be activated at the same time as this group) to deactivate. See DestinyTalentExclusiveGroup for more information on the details. This is an identifier for this node's group, if it is part of one.
ignore_for_completion: t.Optional[
bool
] = None # Comes from the talent grid node style: if true, then this node should be ignored for determining whether the grid is complete.
is_random: t.Optional[
bool
] = None # If this is true, the node's step is determined randomly rather than the first step being chosen.
is_random_repurchasable: t.Optional[
bool
] = None # If this is true, the node can be "re-rolled" to acquire a different random current step. This is not used, but still exists for a theoretical future of talent grids.
last_step_repeats: t.Optional[
bool
] = None # At one point, Nodes were going to be able to be activated multiple times, changing the current step and potentially piling on multiple effects from the previously activated steps. This property would indicate if the last step could be activated multiple times. This is not currently used, but it isn't out of the question that this could end up being used again in a theoretical future.
layout_identifier: t.Optional[
str
] = None # A string identifier for a custom visual layout to apply to this talent node. Unfortunately, we do not have any data for rendering these custom layouts. It will be up to you to interpret these strings and change your UI if you want to have custom UI matching these layouts.
lore_hash: t.Optional[
int
] = None # Talent nodes can be associated with a piece of Lore, generally rendered in a tooltip. This is the hash identifier of the lore element to show, if there is one to be show.
node_hash: t.Optional[
int
] = None # The hash identifier for the node, which unfortunately is also content version dependent but can be (and ideally, should be) used instead of the nodeIndex to uniquely identify the node. The two exist side-by-side for backcompat reasons due to the Great Talent Node Restructuring of Destiny 1, and I ran out of time to remove one of them and standardize on the other. Sorry!
node_index: t.Optional[
int
] = None # The index into the DestinyTalentGridDefinition's "nodes" property where this node is located. Used to uniquely identify the node within the Talent Grid. Note that this is content version dependent: make sure you have the latest version of content before trying to use these properties.
node_style_identifier: t.Optional[
str
] = None # Comes from the talent grid node style: this identifier should be used to determine how to render the node in the UI.
prerequisite_node_indexes: t.Optional[
t.Sequence[int]
] = None # Indexes into the DestinyTalentGridDefinition.nodes property for any nodes that must be activated before this one is allowed to be activated. I would have liked to change this to hashes for Destiny 2, but we have run out of time.
random_activation_requirement: t.Optional[
"DestinyNodeActivationRequirement"
] = None # At one point, you were going to be able to repurchase talent nodes that had random steps, to "re-roll" the current step of the node (and thus change the properties of your item). This was to be the activation requirement for performing that re-roll. The system still exists to do this, as far as I know, so it may yet come back around!
random_start_progression_bar_at_progression: t.Optional[
int
] = None # If the node's step is randomly selected, this is the amount of the Talent Grid's progression experience at which the progression bar for the node should be shown.
row: t.Optional[
int
] = None # The visual "row" where the node should be shown in the UI. If negative, then the node is hidden.
steps: t.Optional[
t.Sequence["DestinyNodeStepDefinition"]
] = None # At this point, "steps" have been obfuscated into conceptual entities, aggregating the underlying notions of "properties" and "true steps". If you need to know a step as it truly exists - such as when recreating Node logic when processing Vendor data - you'll have to use the "realSteps" property below.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"nodeIndex": to_json(self.node_index),
"nodeHash": to_json(self.node_hash),
"row": to_json(self.row),
"column": to_json(self.column),
"prerequisiteNodeIndexes": to_json(self.prerequisite_node_indexes),
"binaryPairNodeIndex": to_json(self.binary_pair_node_index),
"autoUnlocks": to_json(self.auto_unlocks),
"lastStepRepeats": to_json(self.last_step_repeats),
"isRandom": to_json(self.is_random),
"randomActivationRequirement": to_json(self.random_activation_requirement),
"isRandomRepurchasable": to_json(self.is_random_repurchasable),
"steps": to_json(self.steps),
"exclusiveWithNodeHashes": to_json(self.exclusive_with_node_hashes),
"randomStartProgressionBarAtProgression": to_json(
self.random_start_progression_bar_at_progression
),
"layoutIdentifier": to_json(self.layout_identifier),
"groupHash": to_json(self.group_hash),
"loreHash": to_json(self.lore_hash),
"nodeStyleIdentifier": to_json(self.node_style_identifier),
"ignoreForCompletion": to_json(self.ignore_for_completion),
}
[docs]@dt.dataclass(frozen=True)
class DestinyNodeActivationRequirement:
"""Talent nodes have requirements that must be met before they can be
activated.
This describes the material costs, the Level of the Talent Grid's
progression required, and other conditional information that limits
whether a talent node can be activated.
"""
grid_level: t.Optional[
int
] = None # The Progression level on the Talent Grid required to activate this node. See DestinyTalentGridDefinition.progressionHash for the related Progression, and read DestinyProgressionDefinition's documentation to learn more about Progressions.
material_requirement_hashes: t.Optional[
t.Sequence[int]
] = None # The list of hash identifiers for material requirement sets: materials that are required for the node to be activated. See DestinyMaterialRequirementSetDefinition for more information about material requirements. In this case, only a single DestinyMaterialRequirementSetDefinition will be chosen from this list, and we won't know which one will be chosen until an instance of the item is created.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"gridLevel": to_json(self.grid_level),
"materialRequirementHashes": to_json(self.material_requirement_hashes),
}
[docs]@dt.dataclass(frozen=True)
class DestinyNodeStepDefinition:
"""This defines the properties of a "Talent Node Step". When you see a
talent node in game, the actual visible properties that you see (its icon,
description, the perks and stats it provides) are not provided by the Node
itself, but rather by the currently active Step on the node. When a Talent
Node is activated, the currently active step's benefits are conferred upon
the item and character. The currently active step on talent nodes are
determined when an item is first instantiated. Sometimes it is random,
sometimes it is more deterministic (particularly when a node has only a
single step).
Note that, when dealing with Talent Node Steps, you must ensure that you have the latest version of content. stepIndex and nodeStepHash - two ways of identifying the step within a node - are both content version dependent, and thus are subject to change between content updates.
"""
activation_requirement: t.Optional[
"DestinyNodeActivationRequirement"
] = None # If the step has requirements for activation (they almost always do, if nothing else than for the Talent Grid's Progression to have reached a certain level), they will be defined here.
affects_level: t.Optional[
bool
] = None # If true, this step can affect the level of the item. See DestinyInventoryItemDefintion for more information about item levels and their effect on stats.
affects_quality: t.Optional[
bool
] = None # If this is true, the step affects the item's Quality in some way. See DestinyInventoryItemDefinition for more information about the meaning of Quality. I already made a joke about Zen and the Art of Motorcycle Maintenance elsewhere in the documentation, so I will avoid doing it again. Oops too late
can_activate_next_step: t.Optional[
bool
] = None # There was a time when talent nodes could be activated multiple times, and the effects of subsequent Steps would be compounded on each other, essentially "upgrading" the node. We have moved away from this, but theoretically the capability still exists. I continue to return this in case it is used in the future: if true and this step is the current step in the node, you are allowed to activate the node a second time to receive the benefits of the next step in the node, which will then become the active step.
damage_type: t.Optional[
"DamageType"
] = None # An enum representing a damage type granted by activating this step, if any.
damage_type_hash: t.Optional[
int
] = None # If the step provides a damage type, this will be the hash identifier used to look up the damage type's DestinyDamageTypeDefinition.
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # These are the display properties actually used to render the Talent Node. The currently active step's displayProperties are shown.
interaction_description: t.Optional[
str
] = None # If you can interact with this node in some way, this is the localized description of that interaction.
is_next_step_random: t.Optional[
bool
] = None # If true, the next step to be chosen is random, and if you're allowed to activate the next step. (if canActivateNextStep = true)
next_step_index: t.Optional[
int
] = None # The stepIndex of the next step in the talent node, or -1 if this is the last step or if the next step to be chosen is random. This doesn't really matter anymore unless canActivateNextStep begins to be used again.
node_step_hash: t.Optional[
int
] = None # The hash of this node step. Unfortunately, while it can be used to uniquely identify the step within a node, it is also content version dependent and should not be relied on without ensuring you have the latest vesion of content.
perk_hashes: t.Optional[
t.Sequence[int]
] = None # The list of hash identifiers for Perks (DestinySandboxPerkDefinition) that are applied when this step is active. Perks provide a variety of benefits and modifications - examine DestinySandboxPerkDefinition to learn more.
socket_replacements: t.Optional[
t.Sequence["DestinyNodeSocketReplaceResponse"]
] = None # If this step is activated, this will be a list of information used to replace socket items with new Plugs. See DestinyInventoryItemDefinition for more information about sockets and plugs.
start_progression_bar_at_progress: t.Optional[
int
] = None # When the Talent Grid's progression reaches this value, the circular "progress bar" that surrounds the talent node should be shown. This also indicates the lower bound of said progress bar, with the upper bound being the progress required to reach activationRequirement.gridLevel. (at some point I should precalculate the upper bound and put it in the definition to save people time)
stat_hashes: t.Optional[
t.Sequence[int]
] = None # When the step provides stat benefits on the item or character, this is the list of hash identifiers for stats (DestinyStatDefinition) that are provided.
step_groups: t.Optional[
"DestinyTalentNodeStepGroups"
] = None # In Destiny 1, the Armory's Perk Filtering was driven by a concept of TalentNodeStepGroups: categorizations of talent nodes based on their functionality. While the Armory isn't a BNet-facing thing for now, and the new Armory will need to account for Sockets rather than Talent Nodes, this categorization capability feels useful enough to still keep around.
step_index: t.Optional[
int
] = None # The index of this step in the list of Steps on the Talent Node. Unfortunately, this is the closest thing we have to an identifier for the Step: steps are not provided a content version agnostic identifier. This means that, when you are dealing with talent nodes, you will need to first ensure that you have the latest version of content.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"stepIndex": to_json(self.step_index),
"nodeStepHash": to_json(self.node_step_hash),
"interactionDescription": to_json(self.interaction_description),
"damageType": to_json(self.damage_type),
"damageTypeHash": to_json(self.damage_type_hash),
"activationRequirement": to_json(self.activation_requirement),
"canActivateNextStep": to_json(self.can_activate_next_step),
"nextStepIndex": to_json(self.next_step_index),
"isNextStepRandom": to_json(self.is_next_step_random),
"perkHashes": to_json(self.perk_hashes),
"startProgressionBarAtProgress": to_json(
self.start_progression_bar_at_progress
),
"statHashes": to_json(self.stat_hashes),
"affectsQuality": to_json(self.affects_quality),
"stepGroups": to_json(self.step_groups),
"affectsLevel": to_json(self.affects_level),
"socketReplacements": to_json(self.socket_replacements),
}
[docs]@dt.dataclass(frozen=True)
class DestinyNodeSocketReplaceResponse:
"""This is a bit of an odd duck.
Apparently, if talent nodes steps have this data, the game will go
through on step activation and alter the first Socket it finds on
the item that has a type matching the given socket type, inserting
the indicated plug item.
"""
plug_item_hash: t.Optional[
int
] = None # The hash identifier of the plug item that will be inserted into the socket found.
socket_type_hash: t.Optional[
int
] = None # The hash identifier of the socket type to find amidst the item's sockets (the item to which this talent grid is attached). See DestinyInventoryItemDefinition.sockets.socketEntries to find the socket type of sockets on the item in question.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"socketTypeHash": to_json(self.socket_type_hash),
"plugItemHash": to_json(self.plug_item_hash),
}
[docs]@dt.dataclass(frozen=True)
class DestinyDamageTypeDefinition:
"""All damage types that are possible in the game are defined here, along
with localized info and icons as needed."""
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # The description of the damage type, icon etc...
enum_value: t.Optional[
"DamageType"
] = None # We have an enumeration for damage types for quick reference. This is the current definition's damage type enum value.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
show_icon: t.Optional[
bool
] = None # If TRUE, the game shows this damage type's icon. Otherwise, it doesn't. Whether you show it or not is up to you.
transparent_icon_path: t.Optional[
str
] = None # A variant of the icon that is transparent and colorless.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"transparentIconPath": to_json(self.transparent_icon_path),
"showIcon": to_json(self.show_icon),
"enumValue": to_json(self.enum_value),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyTalentNodeExclusiveSetDefinition:
"""The list of indexes into the Talent Grid's "nodes" property for nodes in
this exclusive set.
(See DestinyTalentNodeDefinition.nodeIndex)
"""
node_indexes: t.Optional[
t.Sequence[int]
] = None # The list of node indexes for the exclusive set. Historically, these were indexes. I would have liked to replace this with nodeHashes for consistency, but it's way too late for that. (9:09 PM, he's right!)
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"nodeIndexes": to_json(self.node_indexes),
}
[docs]@dt.dataclass(frozen=True)
class DestinyTalentExclusiveGroup:
"""As of Destiny 2, nodes can exist as part of "Exclusive Groups".
These differ from exclusive sets in that, within the group, many
nodes can be activated. But the act of activating any node in the
group will cause "opposing" nodes (nodes in groups that are not
allowed to be activated at the same time as this group) to
deactivate.
"""
group_hash: t.Optional[
int
] = None # The identifier for this exclusive group. Only guaranteed unique within the talent grid, not globally.
lore_hash: t.Optional[
int
] = None # If this group has an associated piece of lore to show next to it, this will be the identifier for that DestinyLoreDefinition.
node_hashes: t.Optional[
t.Sequence[int]
] = None # A quick reference of the talent nodes that are part of this group, by their Talent Node hashes. (See DestinyTalentNodeDefinition.nodeHash)
opposing_group_hashes: t.Optional[
t.Sequence[int]
] = None # A quick reference of Groups whose nodes will be deactivated if any node in this group is activated.
opposing_node_hashes: t.Optional[
t.Sequence[int]
] = None # A quick reference of Nodes that will be deactivated if any node in this group is activated, by their Talent Node hashes. (See DestinyTalentNodeDefinition.nodeHash)
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"groupHash": to_json(self.group_hash),
"loreHash": to_json(self.lore_hash),
"nodeHashes": to_json(self.node_hashes),
"opposingGroupHashes": to_json(self.opposing_group_hashes),
"opposingNodeHashes": to_json(self.opposing_node_hashes),
}
[docs]@dt.dataclass(frozen=True)
class DestinyTalentNodeCategory:
"""An artificial construct provided by Bungie.Net, where we attempt to
group talent nodes by functionality.
This is a single set of references to Talent Nodes that share a
common trait or purpose.
"""
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # Will contain at least the "name", which will be the title of the category. We will likely not have description and an icon yet, but I'm going to keep my options open.
identifier: t.Optional[
str
] = None # Mostly just for debug purposes, but if you find it useful you can have it. This is BNet's manually created identifier for this category.
is_lore_driven: t.Optional[
bool
] = None # If true, we found the localized content in a related DestinyLoreDefinition instead of local BNet localization files. This is mostly for ease of my own future investigations.
node_hashes: t.Optional[
t.Sequence[int]
] = None # The set of all hash identifiers for Talent Nodes (DestinyTalentNodeDefinition) in this Talent Grid that are part of this Category.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"identifier": to_json(self.identifier),
"isLoreDriven": to_json(self.is_lore_driven),
"displayProperties": to_json(self.display_properties),
"nodeHashes": to_json(self.node_hashes),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemPerkEntryDefinition:
"""An intrinsic perk on an item, and the requirements for it to be
activated."""
perk_hash: t.Optional[
int
] = None # A hash identifier for the DestinySandboxPerkDefinition being provided on the item.
perk_visibility: t.Optional[
"ItemPerkVisibility"
] = None # Indicates whether this perk should be shown, or if it should be shown disabled.
requirement_display_string: t.Optional[
str
] = None # If this perk is not active, this is the string to show for why it's not providing its benefits.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"requirementDisplayString": to_json(self.requirement_display_string),
"perkHash": to_json(self.perk_hash),
"perkVisibility": to_json(self.perk_visibility),
}
[docs]@dt.dataclass(frozen=True)
class DestinyItemCategoryDefinition:
"""In an attempt to categorize items by type, usage, and other interesting
properties, we created DestinyItemCategoryDefinition: information about
types that is assembled using a set of heuristics that examine the
properties of an item such as what inventory bucket it's in, its item type
name, and whether it has or is missing certain blocks of data.
This heuristic is imperfect, however. If you find an item
miscategorized, let us know on the Bungie API forums! We then
populate all of the categories that we think an item belongs to in
its DestinyInventoryItemDefinition.itemCategoryHashes property. You
can use that to provide your own custom item filtering, sorting,
aggregating... go nuts on it! And let us know if you see more
categories that you wish would be added!
"""
deprecated: t.Optional[
bool
] = None # If True, this category has been deprecated: it may have no items left, or there may be only legacy items that remain in it which are no longer relevant to the game.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
grant_destiny_breaker_type: t.Optional[
"DestinyBreakerType"
] = None # If the item in question has this category, it also should have this breaker type.
grant_destiny_class: t.Optional[
"DestinyClass"
] = None # If an item belongs to this category, it will also get this class restriction enum value. See the other "grant"-prefixed properties on this definition for my color commentary.
grant_destiny_item_type: t.Optional[
"DestinyItemType"
] = None # If an item belongs to this category, it will also receive this item type. This is now how DestinyItemType is populated for items: it used to be an even jankier process, but that's a story that requires more alcohol.
grant_destiny_sub_type: t.Optional[
"DestinyItemSubType"
] = None # If an item belongs to this category, it will also receive this subtype enum value. I know what you're thinking - what if it belongs to multiple categories that provide sub-types? The last one processed wins, as is the case with all of these "grant" enums. Now you can see one reason why we moved away from these enums... but they're so convenient when they work, aren't they?
group_category_only: t.Optional[
bool
] = None # If true, this category is only used for grouping, and should not be evaluated with its own checks. Rather, the item only has this category if it has one of its child categories.
grouped_category_hashes: t.Optional[
t.Sequence[int]
] = None # If this category is a "parent" category of other categories, those children will have their hashes listed in rendering order here, and can be looked up using these hashes against DestinyItemCategoryDefinition. In this way, you can build up a visual hierarchy of item categories. That's what we did, and you can do it too. I believe in you. Yes, you, Carl. (I hope someone named Carl reads this someday)
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
item_type_regex: t.Optional[
str
] = None # The janky regular expression we used against the item type to try and discern whether the item belongs to this category.
item_type_regex_not: t.Optional[
str
] = None # If the item type matches this janky regex, it does *not* belong to this category.
origin_bucket_identifier: t.Optional[
str
] = None # If the item belongs to this bucket, it does belong to this category.
parent_category_hashes: t.Optional[
t.Sequence[int]
] = None # All item category hashes of "parent" categories: categories that contain this as a child through the hierarchy of groupedCategoryHashes. It's a bit redundant, but having this child-centric list speeds up some calculations.
plug_category_identifier: t.Optional[
str
] = None # If the item is a plug, this is the identifier we expect to find associated with it if it is in this category.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
short_title: t.Optional[
str
] = None # A shortened version of the title. The reason why we have this is because the Armory in German had titles that were too long to display in our UI, so these were localized abbreviated versions of those categories. The property still exists today, even though the Armory doesn't exist for D2... yet.
trait_id: t.Optional[
str
] = None # The traitId that can be found on items that belong to this category.
visible: t.Optional[
bool
] = None # If True, this category should be visible in UI. Sometimes we make categories that we don't think are interesting externally. It's up to you if you want to skip on showing them.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"visible": to_json(self.visible),
"deprecated": to_json(self.deprecated),
"shortTitle": to_json(self.short_title),
"itemTypeRegex": to_json(self.item_type_regex),
"grantDestinyBreakerType": to_json(self.grant_destiny_breaker_type),
"plugCategoryIdentifier": to_json(self.plug_category_identifier),
"itemTypeRegexNot": to_json(self.item_type_regex_not),
"originBucketIdentifier": to_json(self.origin_bucket_identifier),
"grantDestinyItemType": to_json(self.grant_destiny_item_type),
"grantDestinySubType": to_json(self.grant_destiny_sub_type),
"grantDestinyClass": to_json(self.grant_destiny_class),
"traitId": to_json(self.trait_id),
"groupedCategoryHashes": to_json(self.grouped_category_hashes),
"parentCategoryHashes": to_json(self.parent_category_hashes),
"groupCategoryOnly": to_json(self.group_category_only),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyProgressionRewardItemQuantity:
acquisition_behavior: t.Optional[
"DestinyProgressionRewardItemAcquisitionBehavior"
] = None
claim_unlock_display_strings: t.Optional[t.Sequence[str]] = None
has_conditional_visibility: t.Optional[
bool
] = None # Indicates that this item quantity may be conditionally shown or hidden, based on various sources of state. For example: server flags, account state, or character progress.
item_hash: t.Optional[
int
] = None # The hash identifier for the item in question. Use it to look up the item's DestinyInventoryItemDefinition.
item_instance_id: t.Optional[
int
] = None # If this quantity is referring to a specific instance of an item, this will have the item's instance ID. Normally, this will be null.
quantity: t.Optional[
int
] = None # The amount of the item needed/available depending on the context of where DestinyItemQuantity is being used.
rewarded_at_progression_level: t.Optional[int] = None
ui_display_style: t.Optional[str] = None
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"rewardedAtProgressionLevel": to_json(self.rewarded_at_progression_level),
"acquisitionBehavior": to_json(self.acquisition_behavior),
"uiDisplayStyle": to_json(self.ui_display_style),
"claimUnlockDisplayStrings": to_json(self.claim_unlock_display_strings),
"itemHash": to_json(self.item_hash),
"itemInstanceId": to_json(self.item_instance_id),
"quantity": to_json(self.quantity),
"hasConditionalVisibility": to_json(self.has_conditional_visibility),
}
[docs]@dt.dataclass(frozen=True)
class DestinyRaceDefinition:
"""In Destiny, "Races" are really more like "Species".
Sort of. I mean, are the Awoken a separate species from humans? I'm
not sure. But either way, they're defined here. You'll see Exo,
Awoken, and Human as examples of these Species. Players will choose
one for their character.
"""
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
gendered_race_names: t.Optional[
t.Mapping[str, str]
] = None # A localized string referring to the singular form of the Race's name when referred to in gendered form. Keyed by the DestinyGender.
gendered_race_names_by_gender_hash: t.Optional[t.Mapping[str, str]] = None
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
race_type: t.Optional[
"DestinyRace"
] = None # An enumeration defining the existing, known Races/Species for player characters. This value will be the enum value matching this definition.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"raceType": to_json(self.race_type),
"genderedRaceNames": to_json(self.gendered_race_names),
"genderedRaceNamesByGenderHash": to_json(
self.gendered_race_names_by_gender_hash
),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyClassDefinition:
"""Defines a Character Class in Destiny 2.
These are types of characters you can play, like Titan, Warlock, and
Hunter.
"""
class_type: t.Optional[
"DestinyClass"
] = None # In Destiny 1, we added a convenience Enumeration for referring to classes. We've kept it, though mostly for posterity. This is the enum value for this definition's class.
display_properties: t.Optional["DestinyDisplayPropertiesDefinition"] = None
gendered_class_names: t.Optional[
t.Mapping[str, str]
] = None # A localized string referring to the singular form of the Class's name when referred to in gendered form. Keyed by the DestinyGender.
gendered_class_names_by_gender_hash: t.Optional[t.Mapping[str, str]] = None
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
mentor_vendor_hash: t.Optional[
int
] = None # Mentors don't really mean anything anymore. Don't expect this to be populated.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"classType": to_json(self.class_type),
"displayProperties": to_json(self.display_properties),
"genderedClassNames": to_json(self.gendered_class_names),
"genderedClassNamesByGenderHash": to_json(
self.gendered_class_names_by_gender_hash
),
"mentorVendorHash": to_json(self.mentor_vendor_hash),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyUnlockDefinition:
"""Unlock Flags are small bits (literally, a bit, as in a boolean value)
that the game server uses for an extremely wide range of state checks,
progress storage, and other interesting tidbits of information."""
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # Sometimes, but not frequently, these unlock flags also have human readable information: usually when they are being directly tested for some requirement, in which case the string is a localized description of why the requirement check failed.
hash: t.Optional[
int
] = None # The unique identifier for this entity. Guaranteed to be unique for the type of entity, but not globally. When entities refer to each other in Destiny content, it is this hash that they are referring to.
index: t.Optional[
int
] = None # The index of the entity as it was found in the investment tables.
redacted: t.Optional[
bool
] = None # If this is true, then there is an entity with this identifier/type combination, but BNet is not yet allowed to show it. Sorry!
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"displayProperties": to_json(self.display_properties),
"hash": to_json(self.hash),
"index": to_json(self.index),
"redacted": to_json(self.redacted),
}
[docs]@dt.dataclass(frozen=True)
class DestinyEntitySearchResult:
"""The results of a search for Destiny content.
This will be improved on over time, I've been doing some
experimenting to see what might be useful.
"""
results: t.Optional[
"SearchResultOfDestinyEntitySearchResultItem"
] = None # The items found that are matches/near matches for the searched-for term, sorted by something vaguely resembling "relevance". Hopefully this will get better in the future.
suggested_words: t.Optional[
t.Sequence[str]
] = None # A list of suggested words that might make for better search results, based on the text searched for.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"suggestedWords": to_json(self.suggested_words),
"results": to_json(self.results),
}
[docs]@dt.dataclass(frozen=True)
class DestinyEntitySearchResultItem:
"""An individual Destiny Entity returned from the entity search."""
display_properties: t.Optional[
"DestinyDisplayPropertiesDefinition"
] = None # Basic display properties on the entity, so you don't have to look up the definition to show basic results for the item.
entity_type: t.Optional[
str
] = None # The type of entity, returned as a string matching the DestinyDefinition's contract class name. You'll have to have your own mapping from class names to actually looking up those definitions in the manifest databases.
hash: t.Optional[
int
] = None # The hash identifier of the entity. You will use this to look up the DestinyDefinition relevant for the entity found.
weight: t.Optional[
float
] = None # The ranking value for sorting that we calculated using our relevance formula. This will hopefully get better with time and iteration.
[docs] def to_json(self) -> t.Mapping[str, t.Any]:
return {
"hash": to_json(self.hash),
"entityType": to_json(self.entity_type),
"displayProperties": to_json(self.display_properties),
"weight": to_json(self.weight),
}
from bungieapi.generated.components.schemas import BungieMembershipType # noqa: E402
from bungieapi.generated.components.schemas import (
SearchResultOfDestinyEntitySearchResultItem,
) # noqa: E402
from bungieapi.generated.components.schemas.dates import DateRange # noqa: E402
# imported at the end to do not case circular imports for type annotations
from bungieapi.generated.components.schemas.destiny import BucketCategory # noqa: E402
from bungieapi.generated.components.schemas.destiny import BucketScope # noqa: E402
from bungieapi.generated.components.schemas.destiny import DamageType # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyActivityModeCategory,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyActivityNavPointType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyAmmunitionType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyBreakerType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import DestinyClass # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyGatingScope,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import DestinyGender # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyItemQuantity,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyItemSortType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyItemSubType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import DestinyItemType # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyObjectiveGrantStyle,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyProgressionRewardItemAcquisitionBehavior,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyProgressionScope,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyProgressionStepDisplayEffect,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import DestinyRace # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyStatAggregationType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyStatCategory,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyUnlockValueUIStyle,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyVendorInteractionRewardSelection,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyVendorItemRefundPolicy,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyVendorProgressionType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
DestinyVendorReplyType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import DyeReference # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
EquippingItemBlockAttributes,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import ItemLocation # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
ItemPerkVisibility,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
SocketPlugSources,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import SpecialItemType # noqa: E402
from bungieapi.generated.components.schemas.destiny import TierType # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
VendorDisplayCategorySortOrder,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny import (
VendorInteractionType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.constants import (
DestinyEnvironmentLocationMapping,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.definitions.animations import (
DestinyAnimationReference,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.definitions.common import (
DestinyDisplayPropertiesDefinition,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.definitions.common import (
DestinyIconSequenceDefinition,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.definitions.items import (
DestinyDerivedItemCategoryDefinition,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.definitions.items import (
DestinyItemPlugDefinition,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.definitions.sources import (
DestinyItemSourceDefinition,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.definitions.vendors import (
DestinyVendorLocationDefinition,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.historical_stats.definitions import (
DestinyActivityModeType,
) # noqa: E402
from bungieapi.generated.components.schemas.destiny.misc import (
DestinyColor,
) # noqa: E402
from bungieapi.generated.components.schemas.interpolation import (
InterpolationPoint,
) # noqa: E402
from bungieapi.generated.components.schemas.links import (
HyperlinkReference,
) # noqa: E402