ИИ питомцев переписан, добавлен Дух крысы, пороги разнесены по шкале
Работа по указаниям 2026-09-18. В релиз пока не выходит. НОВЫЙ ПИТОМЕЦ "Дух крысы" (necroRatSpirit), Некромантия 3 (60 убийств): модель зомбоволка в масштабе 0.28, урон 5, кроличьи звуки. Сам не нападает никогда, держится справа-сзади в двух блоках, упёршись в препятствие проходит СКВОЗЬ него и блоков не ломает. Укус замедляет, оставляет метку духа (видна на компасе, +25% получаемого урона) и рвёт жилу. Обгрызая труп, лечится. СВОЯ ЗАДАЧА ИИ. Задачи "иди за сущностью" в игре нет вовсе - проверены все 32 типа EAI*. Написана NecroFollowOwnerTask по образцу EAIApproachSpot: сектор "3-6 часов" от хозяина, FindPath с canBreak:false, проход сквозь препятствие через Entity.IsStuck. Вешается в рантайме, минуя Type.GetType. ПРИКАЗ АТАКОВАТЬ. Повторное применение свитка при живом питомце натравливает его на цель под прицелом: EntityPlayerLocal.HitInfo + ItemActionAttack. GetEntityFromHit. Торговцы и игроки отсеяны. Нет цели - "Нет цели для атаки". ЗОМБОЖИВОТНЫЕ ПРИВЕДЕНЫ К ТОМУ ЖЕ ОБРАЗЦУ. Сняты BreakBlock, Territorial, ApproachSpot, Wander, BlockingTargetTask и поедание трупов; цель они больше не выбирают сами. Лестница урона 20/35/45/60 плюс расчленение у Пса, Медведя и Волка. Кровотечение всем, метка и ослабление - только у крысы. УБИЙСТВА ПИТОМЦЕМ ЗАСЧИТЫВАЮТСЯ ВЛАДЕЛЬЦУ, включая добивание кровотечением. Префикс на AwardKillXPServer подменяет убийцу владельцем; для смерти от баффа заведена память укусов, потому что в DamageSource от баффа нет того, кто его наложил. Зомби под Камнем духов это не задело - решение от 17.09 в силе. ГРИФ откачен на летающую ветку EntityVulture и переименован в Могильного стервятника: модель наконец соответствует имени. Держится у игрока сам, через собственный механизм "дома" (setHomeArea), на время погони дом отвязывается. Попытка натянуть птичий префаб на наземный класс провалилась и записана - так делать нельзя. ПРОЧЕЕ: призрачный вид распространён с торговцев на питомцев (у Пса, Медведя и Волка выключен по указанию), у Пса светятся фиолетовые глаза, белая иконка книги снята со всех свитков призыва, "Жуки Властелина" переименованы в "Рой фараона" на всех 13 языках. ПОРОГИ: крыса 60, стервятник 500, пёс 1300, медведь 2000, волк 4000. ИСПРАВЛЕНО ПО ХОДУ ИГРОВЫХ ПРОВЕРОК: питомцы не призывались обычным кликом (AnimWait требовал удержания), отзыв срабатывал не с первого раза (автомат состояний ItemActionSpawnEntity), крыса проваливалась сквозь мир (IsStuck отключает и пол), питомец подбрасывал хозяина (коллайдеры разводились до появления модели). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
15c59dd37a
commit
d97f83e972
@@ -266,6 +266,61 @@ namespace NecromancerTome
|
||||
timer = 0f;
|
||||
}
|
||||
|
||||
/// <summary>Кого этот проход обращает в призрака.
|
||||
///
|
||||
/// РАСШИРЕНО 2026-09-18 С ТОРГОВЦЕВ НА ПИТОМЦЕВ, по указанию: "всех призванных существ
|
||||
/// (зомбопёс, зомбомедведь, зомбоволк, зомбогриф) тоже делаем полупрозрачными как сейчас
|
||||
/// торговцев". Плюс Дух крысы, который призраком и задуман.
|
||||
///
|
||||
/// Обобщение вышло механическим, и это не совпадение: ApplyGreyscale ниже никогда не
|
||||
/// пользовался ничем трейдерским - только GetComponentsInChildren<Renderer>(),
|
||||
/// EntityClass.entityClassName и entityId, а всё это есть у любой EntityAlive.
|
||||
///
|
||||
/// СПИСОК БЕРЁТСЯ ИЗ SummonPatch.LimitedPets, а не дублируется здесь: новый питомец
|
||||
/// становится призраком автоматически, в одном месте, и разойтись эти два списка не могут.
|
||||
/// Рой жуков в нём тоже есть, но он безвреден - его рендереры это системы частиц, а
|
||||
/// ApplyGreyscale их намеренно пропускает (см. Convert), так что для него проход вхолостую.
|
||||
///
|
||||
/// Проверка по имени класса, а не по типу: все наземные питомцы мода - это C#-класс
|
||||
/// EntityZombieDog (см. entityclasses.xml), то есть по типу их не отличить ни друг от
|
||||
/// друга, ни от настоящего зомбопса из мира.</summary>
|
||||
/// <summary>Исключения из призрачного вида. Зомбопёс - по прямому указанию 2026-09-18:
|
||||
/// "зомбособаку не делай полупрозрачной или серой, пусть будет обычной".
|
||||
///
|
||||
/// Проверка идёт по ИМЕНИ КЛАССА, а не по типу, и это здесь важно: Дух крысы наследуется
|
||||
/// от necroZombieWolf, а тот от necroZombieDog, то есть по типу они все EntityZombieDog и
|
||||
/// исключение задело бы заодно и крысу, которая призраком как раз и задумана.</summary>
|
||||
public static readonly HashSet<string> NotGhosted = new HashSet<string>
|
||||
{
|
||||
"necroZombieDog",
|
||||
// Медведь и Волк добавлены 2026-09-18: "прозрачность им не нужна". Призрачными
|
||||
// остаются Дух крысы, Зомбогриф и торговцы.
|
||||
"necroZombieBear",
|
||||
"necroZombieWolf",
|
||||
// Гриф выведен 18.09 вместе с откатом: "не делай его прозрачным". На птичьей модели
|
||||
// обесцвечивание к тому же брало не всё - прозрачными выходили только туловище и
|
||||
// голова, что и выглядело жутко.
|
||||
"necroZombieGriffin",
|
||||
};
|
||||
|
||||
public static bool ShouldBeGhost(EntityAlive _entity)
|
||||
{
|
||||
if (_entity is EntityTrader)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
EntityClass entityClass = EntityClass.list[_entity.entityClass];
|
||||
if (entityClass == null || entityClass.entityClassName == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (NotGhosted.Contains(entityClass.entityClassName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Patch_ItemActionSpawnEntity_Spawn_PetLimit.LimitedPets.ContainsKey(entityClass.entityClassName);
|
||||
}
|
||||
|
||||
public static void OnUnityUpdate(ref ModEvents.SUnityUpdateData _data)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
@@ -284,11 +339,11 @@ namespace NecromancerTome
|
||||
for (int i = 0; i < world.EntityAlives.Count; i++)
|
||||
{
|
||||
EntityAlive entity = world.EntityAlives[i];
|
||||
if (!(entity is EntityTrader trader) || trader.IsDead())
|
||||
if (entity == null || entity.IsDead() || !ShouldBeGhost(entity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Ghosted.TryGetValue(trader.entityId, out GhostBody body))
|
||||
if (Ghosted.TryGetValue(entity.entityId, out GhostBody body))
|
||||
{
|
||||
if (IsIntact(body))
|
||||
{
|
||||
@@ -297,14 +352,14 @@ namespace NecromancerTome
|
||||
// His model was destroyed and rebuilt under him. Drop what is known about the
|
||||
// old one before building the new, or Converted and TintedMaterials keep
|
||||
// entries for renderers and materials that no longer exist.
|
||||
Debug.Log("[NecromancerTome] GhostTraderPatch: entity " + trader.entityId +
|
||||
Debug.Log("[NecromancerTome] GhostTraderPatch: entity " + entity.entityId +
|
||||
" came back with a new model - ghosting him again");
|
||||
Ghosted.Remove(trader.entityId);
|
||||
Ghosted.Remove(entity.entityId);
|
||||
Prune();
|
||||
}
|
||||
if (ApplyGreyscale(trader, out GhostBody fresh))
|
||||
if (ApplyGreyscale(entity, out GhostBody fresh))
|
||||
{
|
||||
Ghosted[trader.entityId] = fresh;
|
||||
Ghosted[entity.entityId] = fresh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,7 +418,7 @@ namespace NecromancerTome
|
||||
/// leaves this trader unmarked and tries again on the next sweep. On true, _body carries
|
||||
/// the renderers taken over, which is how the next sweep tells this trader from a rebuilt
|
||||
/// one standing under the same entity id.</summary>
|
||||
public static bool ApplyGreyscale(EntityTrader _trader, out GhostBody _body)
|
||||
public static bool ApplyGreyscale(EntityAlive _trader, out GhostBody _body)
|
||||
{
|
||||
_body = default(GhostBody);
|
||||
Renderer[] renderers = _trader.GetComponentsInChildren<Renderer>(true);
|
||||
@@ -797,6 +852,21 @@ namespace NecromancerTome
|
||||
break;
|
||||
}
|
||||
|
||||
// НАЙДЕНО 2026-09-18, НО НЕ ИСПРАВЛЕНО - записано, чтобы не искать заново.
|
||||
// Пользователь: "собака не полупрозрачная". По логу у неё для этого всё было -
|
||||
// шейдер Standard с _Color, _SrcBlend, _DstBlend, "5 of 5 renderer(s) desaturated,
|
||||
// 5 opacity lever(s) installed". То есть альфа проставлена, а прозрачности нет.
|
||||
//
|
||||
// Разница с трейдерами, у которых она работает: их тело идёт по ДРУГОЙ ветке -
|
||||
// MakeBlendMaterial создаёт материал заново и ставит там _Mode = 2 (Fade). Здесь,
|
||||
// на ветке "блендим на месте", _Mode и _ZWrite не трогаются вовсе, а материал,
|
||||
// собранный как Opaque, приходит с _Mode = 0 и _ZWrite = 1. Юнити для перевода
|
||||
// Standard в прозрачность требует всю шестёрку: _Mode, _SrcBlend, _DstBlend,
|
||||
// _ZWrite, ключевые слова и очередь отрисовки - у нас выставлены четыре из шести.
|
||||
//
|
||||
// Собаку это чинить больше не нужно (она выведена из призраков совсем), но Медведь,
|
||||
// Волк и Гриф идут по этой же ветке. Если и они окажутся непрозрачными - причина
|
||||
// здесь, и лечится добавлением _Mode = 2 и _ZWrite = 0 рядом со строками ниже.
|
||||
if (_material.HasProperty("_SrcBlend") && _material.HasProperty("_DstBlend"))
|
||||
{
|
||||
_material.SetFloat("_SrcBlend", (float)BlendMode.SrcAlpha);
|
||||
|
||||
Reference in New Issue
Block a user