Решили: облачение (OutfitData) прячет часть тела, обнуляя её спрайт, а не выключая
рендерер; надевается в UnitView.InitVisual и зовётся ДАЖЕ когда облачения нет; раздаётся юнитам по
механике, а не по списку имён.
Почему: заказ «щиты только у носителя» — первый частный случай системы оружия (Planning - Weapon System §3). Отвергнуты: отдельный префаб-вариант без щита (хореография «меча и щита» от наличия щита в кадре не меняется, а вариантов пришлось бы плодить по числу сочетаний) и отдельный архетип «только меч» (это дубль набора клипов ради одного пустого спрайта).
Грабли:
enabled— чужой канал, и он бы всё сломал.SetVisibleгасит и зажигает ВСЕ части разом: разлёт осколков, возврат вида в пул. Спрячь щит выключателем — первый же возврат видимости надел бы его обратно, причём не сразу, а через смерть-возрождение, то есть выглядело бы как «иногда щит появляется». Спрайт этим каналом не пользуется, поэтому прячем им.- Облачение обязано зваться и когда его НЕТ. Вид переиспользуется пулом: без явного возврата к
префабным спрайтам Защитник, занявший вид убитого лучника, вышел бы на арену без щита. Отсюда
снимок
_bareSprites— точка возврата, снимаемая один раз до первого облачения. - Два разных «ничего» пришлось развести явно. Записи о части нет — часть не трогаем, играет префаб. Запись есть, а спрайт пуст — часть прячем. Без этого различения «облачение» стало бы полной заменой тела, и каждое пришлось бы описывать целиком.
- Раздача по механике поймала мою же ошибку в первый прогон. Гейт (
OutfitCoverageTests) читает «кто носит щит» из кита — у кого эффект-«Оплот» сBlockComponent, — и сразу нашёл дев-дуэлянта: у него кит ЗАЩИТНИКА, а я надела на него «только меч». Список имён такого не заметил бы никогда, а в игре это выглядело бы как «боец блокирует пустой рукой». - Брузер тоже блокирует, но щита у него нет — парирующая стойка. Поэтому признак сужен до эффектов с «Bulwark» в имени: блок вообще ≠ блок щитом.
- Программное
CreateAssetне зовётOnValidate, поэтому id контент-ассета остаётся пустым. Заполнять тем жеContentDomains.MakeId, что и автозаполнение, — иначе ассет попадает в базу с пустым id и валидация ругается уже на нём. - Новый тип контента — это ТРИ места, а не одно: класс,
ContentDomains(тип → домен) иContentPaths(домен → папка). Пропустишь второе — ассет не получит id; пропустишь третье — менеджер положит его вMisc.
Владелец правды: OutfitData (контракт «пусто = спрятать»), SkeletalBodyVisual.ApplyOutfit
(канал спрайта и точка возврата), тест OutfitCoverageTests (обе стороны шва: имя части существует,
щит совпадает с механикой).