Решили: завести в RunState склад предметов забега (ItemInventory) и провести все правки
снаряжения через один глагол SetSlotItem(slotIndex, itemSlot, itemId): надетое уходит со склада,
снятое возвращается туда же. Отдельной команды «переложить от одного к другому» нет — это снятие
плюс надевание.
Почему: страница «Предметы» показывает склад всех доступных вещей
(дизайн §2.2), а хранить его было негде: в RunState жили
только RelicInventory (реликвии) и PartyItemIds. Предметы «Сосуда» существовали исключительно
надетыми — то есть вещь, снятая с бойца, исчезала из мира.
- Прямая команда «переложить» отвергнута: ей нужны четыре числа (откуда, из какого слота, куда,
в какой слот), а команда забега плоская —
SlotIndex,Amount,Text— потому что едет по сети и ложится в лог. Пятое поле ради одного глагола удорожило бы каждую команду. - Проверка «вещь есть в складе» стоит у владельца, а не у нажавшего. Без неё гость, чей снимок
состояния отстал, надел бы уже надетую вещь — и предмет размножился бы. Теперь
SetSlotItemотказывает, если в складе такой вещи нет. - Лимита у склада нет (в отличие от реликвий с
RelicCapacity): дефицит у предметов создают три слота на «Сосуде», а не вместимость запаса — так решено в ГДД про предметы.
Грабли: VesselItemIds в старых забегах короче, чем GameConfig.VesselItemSlots, и обращение
по индексу слота падало бы. Массив дорастает до нужной длины прямо в мутаторе — на чтении это
незаметно, а на записи ломается ровно один раз.
Владелец правды: Scripts/Guild/RunState.cs, Scripts/Guild/RunStateService.cs,
тест RosterCompositionTests.Item_TravelsThroughStash_AndNeverDuplicates.