Skip to content

Итемы

Итемы - это предметы. Внезапно, да?

Предметы делятся на 5 категорий: wearable, holdable, enegry_shield, trinket и clothes. У итемов самих по себе нет никаких методов, но есть поля.

Массовая контейнеризация

Большинство итемов лежат в персонаже/частях тела не напрямую, а в контейнерах. В самом контейнере есть ссылка на оригинальный объект и динамические данные1.

Абстрактные классы

Предметы похожи между собой, поэтому существуют 4 метакласса, которые они наследуют (Python поддерживает множественное наследование). Это все может быть немного путающим поначалу, но мне кажется лучше показать сами классы, чем приводить все-все-все поля в описании каждого предмета.

Класс Item

Все предметы имеют класс Item, как основной.

Поля абстрактного класса Item
uid: str
Уникальный айдишник предмета.
display_name: str
Отображаемое имя.
description: str
Описание предмета.
skill_req: Skills
Инстанс класса скиллов, значения в котором это требования к скиллам для взятия/надевания предмета.
attr_req: Attributes
Аналогично с пунктом выше, только это инстанс класса атрибутов.
tag_req: list[Tag]
Список тэгов, которые необходимы для взятия/надевания предмета.
tag_stop list[Tag]:
Список тэгов, наличие которых запрещает взятие/надевания предмета.
equip_od: int
То, сколько ОД тратится на эквип предмета. Может быть равно нулю.
resists: ResistTable
Резисты, даваемые предметом. Да, даже у holdable есть резисты, будет объяснено ниже, как это работает.
rarity: ItemRarityEnum
Редкость. Она бывает COMMON, UNCOMMON, RARE, EPIC и LEGENDARY.
ars: ArsItemEnum
Подробнее про список арсов можно посмотреть в константах. Это сугубо внутриигровой аспект, который может использоваться в абилках, сам по себе ничего не делает.
tags: list[Tag]
Список тэгов предмета.
tags_type: TagType
Тип тэгов. Для итема это всегда будет TagType.ITEM_TAG
events: dict
Словарь с эвентами, подробнее есть на страничке с ними

Класс SummonedItem

Поля абстрактного класса SummonedItem
time: int
То, на сколько ходов призывается меч.
summoned: bool
Просто буль, который дает понять, призванная эта вещь или нет. Вам никто не запрещает сделать его False, даже если она призванная... Но это не очень мило. Подразумевается, что это полезно для разных абилок и прочего, которые должны воздействовать именно на призванные вещи.

К вопросу о призываемости...

На самом деле, даже если при создании предмета в коде не было указано, что он призванный, в методах взятия/надевания есть возможность задать длительность. То есть даже обычный меч можно взять, как призванный. Для этого и нужен буль, чтобы однозначно отделить по натуре призванный от непризванного.

Класс DurabilityItem

Поля абстрактного класса DurabilityItem
durability: float
Максимальная прочность предмета.
durability_item: str
Какой итем нужен для починки предмета. Тут это чисто для хранения, ремонтом должен заниматься внешний клиент.
durability_depletion_rate: float
То, с какой стороностью падает прочность. По умолчанию 1.

Holdable

Это то, что держится в руке. Собственно, holdable это все активируемые предметы, типа мечей, винтовок, гранат. Для упрощения иногда дальше это будет называться "оружием", но типа помните, что это не обязательно оружие.

Он наследует Item, SummonedItem, DurabilityItem, их поля не будут перечислены.

Поля к класса Holdable
two_handed: bool
Является ли оружие двуручным. По умолчанию False. Если оружие двуручное, то вы не можете взять его во вторую руку (offhand) и когда вы его берете в основную, вторая рука занимается псевдоитемом.
ammo: int
Сколько максимум патронов может быть у этого оружия. В рамках этой системы, каждый "патрон" считается одним выстрелом. То есть это может быть как буквально "пуля", так и условные "три пули".
ammo_item: str
Название итема, позволяющего перезарядить оружие. О нем подробнее ниже.
weapon_distance_type: WeaponDistanceType
Энам, бывает MELEE, RANGED, MIXED.
destroy_on_empty: bool
В случае, если True, то после метода юза абилки возвращается инструкция с уничтожением. Подробнее ниже.
transform_item: str
В случае, если тут что-то есть, то после метода юза абилки возвращается инструкция с уничтожением. Подробнее ниже.
possible_upgrades_tags: list[Tag]
Список тэгов, которые означают слоты для апгрейдов. Например, "лезвие" и "рукоятка" или "прицел" и "ствол". Каждый из этих слотов может одновременно быть улучшен одним улучшением. Подробнее про апгрейды ниже.
reload_cost: int
Цена для перезарядки в ОД.
abilities: AbilitiesTable
Абилки, которые дает это оружие.

Как работают резисты оружия

В стандартном эффекте basic_damage смотрится резист предметов в руках и, если их прочность выше 0, он добавляется к резисту в момент нанесения урона. Вы не получите этот резист через обычный метод get_base_resistance() у энтити, и это единственное место, где используется резист у холдабла. Так же холдабл теряет дурабилити, если он снижает урон.

Пример создания оружия:

Holdable(uid="bleeding_sword", display_name="Меч кровотечения",
         abilities=AbilitiesTable(abilities_list=[
             AbilityContainer(ability=Ability.objects(uid="falcon_punch").first()),
             AbilityContainer(ability=Ability.objects(uid="only_while_bleeding").first())
         ]),
         skill_req=Skills(VITAISM=15), durability=50).save()

Апгрейды холдабла (HoldableUpgrade)

Формально он тоже наследуется от ItemUpgrade, но там мало полей, поэтому приведем их тут.

Поля класса HoldableUpgrade
uid: str
Уникальный айдишник апгрейда.
display_name: str
Отображаемое имя апгрейда.
description: str
Описание апгрейда.
upgrade_tag: Tag
Самый главный тэг - тот, по которому проверяется возможность нацепить апгрейд на то или иное оружие.
bonus_damage: int
По умолчанию 0. Бонусный урон.
bonus_precision: float
По умолчанию 0. Бонусная точность.
extra_durability: float
Дополнительная прочность холдабла.
extra_ammo: int
Дополнительные макс. патроны холдабла.
mod_durability_depletion_rate: float
Модификатор к рейтингу падения прочности. Не делайте его отрицательным.
resists: ResistTable
Таблица резистов, добавляющаяся к резистам холдабла.
tags_type: TagType
Для апгрейдов оружия всегда равно TagType.HOLDABLE_UPGRADE_TAG.
tags: list[Tag]
Список тэгов апгрейда оружия.
abilities: AbilitiesTable
Список доп. абилок, даваемых апгрейдом.

Как именно их добавлять, расписано в методах контейнера. У апгрейдов нет методов у самих по себе.

Контейнер для холдаблов (HoldableContainer)

Как и все другие классы итемов, холдаблы лежат в собственном контейнере.

Список полей класса HoldableContainer
item: Holdable
Ссылка на холдабл.
curr_durability: float
Текущая прочность.
curr_ammo: int
Текущее кол-во патронов.
upgrades: list[HoldableUpgrade]
Список апгрейдов.
time_left: int
Времени до исчезновения, если оружие призванное. По умолчанию 0, т.е. оружие не является призванным и никогда не исчезнет.

Недолго живущие контейнеры

Контейнер живет до тех пор, пока энтити держит итем в "руке". Метод API "untake_holdable" возвращает все данные контейнера (итем, прочность, патроны, список апгрейдов) в игру, которая сама должна заниматься менеджментом инвентаря; Spice этого не делает. Так же обращу внимание, что призванные вещи исчезают если их пытаются убрать из руки.

Список методов класса HoldableContainer
get_all_abilities(self, containers: bool = False)
Получить все абилки.
get_all_def_abilities(self, containers: bool = False) - получить итоговую макс. прочность с учетом мода контейнера.
Получить все защитные абилки.
add_upgrade(self, upgrade: HoldableUpgrade) - получить итоговую макс. прочность с учетом мода контейнера.
Добавить апгрейд.
remove_upgrade(self, upgrade: HoldableUpgrade) - получить итоговый деплешен рейт.
Снять апгрейд.

Тонкости апгрейдизации

На самом деле, в реальности не подразумевается что два метода выше используются напрямую. Дело в том, что апгрейды должен ставить игровой клиент. Но для того, чтобы проверить, вообще возможно это или нет, создается временный контейнер оружия и проверяются методы выше. Так что, в принципе, они полностью рабочие и их можно использовать.

get_full_resist(self, name: str)
Получить совокупный резист с учетом всех апгрейдов.
get_max_durability(self) - позволяет забрать патроны, но тут проверка уже будет более строгая, и если текущих патронов меньше, чем требуется, то вернет False. В остальных случаях вернет True, сняв или не сняв патроны в зависимости от just_check.
Получить максимальную прочность с учетом всех апгрейдов.
get_max_ammo(self) - восстанавливает amount патронов и возвращает количество использованных патронов.
Получить максимальный запас патронов с учетом всех апгрейдов.
get_depletion_rate(self)
Получить суммарный рейтинг падения прочности.
get_full_damage(self) -> int
Получить суммированный бонус к точности от всех апгрейдов.
get_full_precision(self) -> float
Получить суммированный бонус к точности от всех апгрейдов.

Расчеты урона у абилок оружия

Три метода выше сами по себе ничего не делают. Ваша абилка оружия должна сама проверять оружие и бонусы от него, исходя из AbilityLocation. Скорее всего для этого будут готовые методы-ютили, но пока их нет.

take_durability(self, amount: float, just_check=False) -> bool
Позволяет взять определенное кол-во прочности, передаваемое в amount. Вернет False только в том случае, если текущее дурабилити меньше или равно 0. Во всех остальных случаях верет True, но если just_check=True, то ничего не сделает, если он False, то снимет этот объем прочности. Таким образом, если, например, на действие нужно 50 прочности, а у вас только 25, оно все равно сработает и вернет True, прочность после этого будет 0. Это ожидаемое поведение и так и должно быть.
repair_durability(self, amount: float)
Чинит предмет на указанную прочность.
reload(self, ammo_item: str, amount: int)
Этот метод используется API, поэтому он возвращает ReturnWrapper. Как он устроен: игровой клиент должен передать итем, которым делается перезарядка и общее количество этого итема у игрока. Этот метод вернет ошибку, если итем неподходящий или оружие нельзя перезарядить. В случае успеха он вернет переменную "ammo_to_take", которая означает то, сколько патронов нужно забрать у игрока. Да, как вы догадались, перезарядка всегда идет до максимума.
reload(self, ammo_item: str, amount: int)
Этот метод используется API, поэтому он возвращает ReturnWrapper. Как он устроен: игровой клиент должен передать итем, которым делается перезарядка и общее количество этого итема у игрока. Этот метод вернет ошибку, если итем неподходящий или оружие нельзя перезарядить. В случае успеха он вернет переменную "ammo_to_take", которая означает то, сколько патронов нужно забрать у игрока. Да, как вы догадались, перезарядка всегда идет до максимума.

Wearable

Это все, что надевается на персонажа. На каждой части тела может быть надет только один экземпляр класса Wearable. Основное назначение брони это добавлять определенные резисты к этой самой конечности.

Он наследует Item, SummonedItem, DurabilityItem, их поля не будут перечислены.

Поля класса Wearable
buffs: Buffs
Баффы, даваемые этим вераблом.
req_bodypart_tag: list[Tag]
Список тэгов, которые ищутся у части тела при надевании верабла. Если есть все указанные тэги, то броня может быть надета.
applied_statuses: list[Status]

Статусы, которые накладываются при надевании этого предмета.

Надеваемые статусы

  • Все подобные статусы выдаются при надевании и подчиняются обычным правилам статусов, т.е. у них снижается duration и всякое такое. То есть вам лучше всего использовать endless=True, если вы не хотите заставлять игроков переодевать предмет.
  • Они накладываются на энтити, а не на часть тела, на которой они надеты.
  • Все такие статусы снимаются по uid в момент снятия верабла. Это значит, что в подавляющем большинстве случаев лучше сделать так, чтобы их не накладывали случайные статусы.
possible_upgrades_tags: list[Tag]
Каждый тэг означает "слот" для прокачки. Работает это точно так же, как у холдаблов.
abilities: AbilitiesTable
Абилки, которые дает этот верабл.

Апгрейды веараблов (WearableUpgrade)

Поля класса WearableUpgrade
buffs: Buffs
Баффы, даваемые этим апгрейдом.
resists: ResistTable
Дополнительные резисты от апгрейда.
abilities: AbilitiesTable
Список абилок, даваемых апгрейдом. Как мы понимаем, абилки у брони - экзотика.
extra_durability: float
Дополнительная прочность.
mod_durability_depletion_rate: float
Модификатор рейта снижения прочности. Не делайте его отрицательным.
req_bodypart_tag: list[Tag]
Дополнительные требуемые тэги для конечности.

Как именно их добавлять, расписано ниже. У апгрейдов нет методов у самих по себе.

Контейнер веараблов (WearableContainer)

Поля класса WearableContainer
item: Wearable
Ссылка на сам веарабл.
curr_durability: float
Текущая прочность брони.
upgrades: list[WearableUpgrade]
Список апгрейдов.
time_left: int
Оставшееся время. Если 0 - итем вечный.

Контейнер вераблов, так же как и остальные контейнеры - просто хранилище для всей динамической инфы.

Методы класса WearableContainer
get_all_abilities(self, containers: bool = False)
Получить все абилки.
get_all_def_abilities(self, containers: bool = False) - получить итоговую макс. прочность с учетом мода контейнера.
Получить все защитные абилки.
add_upgrade(self, upgrade: HoldableUpgrade) - получить итоговую макс. прочность с учетом мода контейнера.
Добавить апгрейд.
remove_upgrade(self, upgrade: HoldableUpgrade) - получить итоговый деплешен рейт.
Снять апгрейд.
take_durability(self, amount: float, just_check=False) -> bool
Вернет false, если в контейнере нет итема или если текущая прочность равна 0. Если она выше нуля, то вычтет всю требуемую сумму (даже если прочности недостаточно, просто установит ее на 0) и вернет True.
get_max_durability(self) -> float - аналогично с аналогичным методом у Holdable.
Получить максимальную прочность.
get_full_resist(self, name: str) -> float
Получить резист с учетом всех апгрейдов.
get_depletion_rate(self) -> float
Получить рейт падения прочности с учетом всех апгрейдов.
repair_durability(self, amount: float)
Чинит предмет на указанную прочность.
`get_adaptation_resist(self, name: str) -> float:
Возвращает суммарную адаптацию c самого верабла и всех апгрейдов.
`get_attribute_value(self, name: str) -> float:
Возвращает суммарный аттрибут c самого верабла и всех апгрейдов.
`get_skill_value(self, name: str) -> float:
Возвращает суммарный скилл c самого верабла и всех апгрейдов.
`get_buff_value(self, name: str) -> float | int:
Возвращает суммарный бафф c самого верабла и всех апгрейдов.

Энергощит (EnergyShield)

Энергощиты - это особые подвиды итемов, которые необязательно являются итемами. Это что-то вроде энергетического поля, окружающее персонажа и дающее ему дополнительное ХП. Обычно в боевых абилках обрабатываются щиты так, что именно по ним наносится урон, а не по ХП, если они есть. У них собственные резисты и на них не влияют статусы и все баффы к резистам энтити - то есть это полностью индивидуальные компоненты с собственными слабостями и сильностями.

Особенность энергощитов

В отличие от холдаблов, вераблов и даже в будущем описываемых тринкетов и одежды, энергощиты не подразумеваются буквально, как итем, который персонаж надевает. Это скорее то, что он их активирует. Энергощиты, в случае их снятия, не возвращают собственные данные, а значит вне персонажа они вообще не существуют. Так же, если ХП щита доходит до 0, то при оценке статуса персонажа, что происходит в конце хода, он безвозвратно уничтожается и заменяется голым контейнером. При этом все абилки, наносящие урон, будут игнорировать щиты, если они есть, но их ХП равен 0. Это сделано специально, чтобы нельзя было накидывать щиты до того (поскольку старый формально все еще существует, хоть и ХП = 0), как сам владелец разрушенного щита хотя бы раз сходит.

Он наследует Item, SummonedItem, их поля не будут перечислены.

Поля класса EnergyShield
hp: int
Максимальное ХП энергощита.
for_mecha: bool
Является ли этот энергощит подходящим для мех. Если да, то он не подходит обычным энтити.

Контейнер энергощитов (EnergyShieldContainer)

Поля класса EnergyShieldContainer
item: EnergyShield
Ссылка на энергощит в этом контейнере.
curr_hp: float
Текущее ХП энергощита.
time_left: int
То, сколько времени осталось у энергощита. Аналогично всем предыдущим штукам со временем. Я бы советовал все энергощиты делать временными, но это не обязательно.
Методы класса EnergyShieldContainer

restore_hp(self, amount: int | float) Ничего не возвращает. Если есть item, то восстанавливает указанный объем ХП, но не может превышать максимум.

take_hp(self, amount: int | float) Ничего не возвращает. Если есть итем, то убирает указанный объем ХП, но он никогда не станет меньше ноля.

Тринкет (Trinket)

Тринкет это особый айтем, который надевается на саму энтити. Он не имеет прочности и по сути работает как статус-эффект, только постоянный. Похож на Wearable, но имеет баффы и не относится к частям тела.

Он наследует Item, их поля не будут перечислены.

Поля класса класса Trinket
buffs: Buffs
Баффы, даваемые этим тринкетом.
applied_statuses: list[Status]
Статусы, даваемые этим тринкетом. Это работает точно так же, как у Вераблов, за подробности смотрите у них.
abilities: AbilitiesTable
Абилки, которые дает этот тринкет.

Тринкеты не имеют методов. У них есть контейнер, правда, вам лучше не знать, что внутри.

Контейнер тринкетов (TrinketContainer)

Поля класса TrinketContainer

Вы уверены, что хотите это знать?

Да...

Это может оказать чудовищное влияние на вашу психику!

Я готов...

Ну что же, представляю вам...

Поля класса TrinketContainer
item: Trinket
Ссылка на тринкет.

Я перевел их в контейнеры на всякий случай. Пока у этого нет четких применений. Но это пока.

Одежда (Clothes)

Одежда похожа на тринкет и представляет из себя... одежду. Она одевается поверх всего остального и в случаях, если это не силовая броня спокойно сосуществует с броней. Тут как в Морровинде, можно носить робы и рубашки вместе с доспехами, да.

Он наследует Item, SummonedItem, DurabilityItem - их поля не будут перечислены.

Поля класса Clothes
beauty: float
Красота одежды! Чем красивее, тем лучше. Не влияет ни на что в Spice, используется Zest.
is_power_armor: bool
Является ли силовой броней. Если да - то эту одежду нельзя надеть вместе с любой броней. Подразумевается, что у нее тогда должны быть мощные резисты и баффы.
buffs: Buffs
Баффы, даваемые одеждой.
adaptation_resists: AdaptationResistTable
Адаптация, даваемая одеждой.
applied_statuses: list[Status]
Статусы, даваемые этим тринкетом. Это работает точно так же, как у Вераблов, за подробности смотрите у них.
abilities: AbilitiesTable
Абилки, которые дает этот тринкет.

Апгрейды одежды (ClothesUpgrade)

Поля класса WearableUpgrade
buffs: Buffs
Баффы, даваемые этим апгрейдом.
resists: ResistTable
Дополнительные резисты от апгрейда.
abilities: AbilitiesTable
Список абилок, даваемых апгрейдом. Как мы понимаем, абилки у брони - экзотика.
extra_durability: float
Дополнительная прочность.
mod_durability_depletion_rate: float
Модификатор рейта снижения прочности. Не делайте его отрицательным.
regen_bonus_percentage: float
Бонус к регенерации одежды. Если будет выше 1 в сумме, то одежда никогда не будет портится.
beauty: float
Бонус к красоте одежды.

Как именно их добавлять, расписано ниже. У апгрейдов нет методов у самих по себе.

Контейнер одежды (ClothesContainer)

Поля класса Clothes
item: Clothes
Ссылка на одежду.
upgrades: [ClothesUpgrades]
Список апгрейдов.
curr_durability: int
Текущая прочность одежды.
time_left: int
Оставшееся время. Если 0 - итем вечный.
Методы класса ClothesContainer
get_all_abilities(self, containers: bool = False)
Получить все абилки.
get_all_def_abilities(self, containers: bool = False) - получить итоговую макс. прочность с учетом мода контейнера.
Получить все защитные абилки.
add_upgrade(self, upgrade: ClothesUpgrade) - получить итоговую макс. прочность с учетом мода контейнера.
Добавить апгрейд.
remove_upgrade(self, upgrade: ClothesUpgrade) - получить итоговый деплешен рейт.
Снять апгрейд.
take_durability(self, amount: float, just_check=False) -> bool
Вернет false, если в контейнере нет итема или если текущая прочность равна 0. Если она выше нуля, то вычтет всю требуемую сумму (даже если прочности недостаточно, просто установит ее на 0) и вернет True.
get_max_durability(self) -> float - аналогично с аналогичным методом у Holdable.
Получить максимальную прочность.
get_full_resist(self, name: str) -> float
Получить резист с учетом всех апгрейдов.
get_depletion_rate(self) -> float
Получить рейт падения прочности с учетом всех апгрейдов.
repair_durability(self, amount: float)
Чинит предмет на указанную прочность.
get_beauty(self, amount: float)
Возвращает значение красоты одежды с учетом апгрейдов.
`get_regen_percentage(self) -> float:
Возвращает значение регена одежды. При каждом получении урона, урон умножается на это число и такое кол-во прочности восстанавливается. Как можно догадаться, если оно больше 1, то одежда никогда не будет ломаться.
`get_adaptation_resist(self, name: str) -> float:
Возвращает суммарную адаптацию c самой одежды и всех апгрейдов.
`get_attribute_value(self, name: str) -> float:
Возвращает суммарный аттрибут c самой одежды и всех апгрейдов.
`get_skill_value(self, name: str) -> float:
Возвращает суммарный скилл c самой одежды и всех апгрейдов.
`get_buff_value(self, name: str) -> float | int:
Возвращает суммарный бафф c самой одежды и всех апгрейдов.

  1. В изначальной версии Spice все предметы были вложенными документами, т.е. полностью хранились в персонаже. Это привело к проблеме того, что если мы хотим поменять у оружия абилку, то оружие не изменялась у всех, у кого это оружие уже было, потому что у каждого была своя индивидуальная копия. В итоге было принято решение перейти на контейнеры, где сами свойства предмета хранятся в виде референса внутри динамического контейнера.