Нож некроманта и Кровь некроманта получили собственные модели, а Пирамида духов - собственную поверхность. Три разных способа, каждый выбран по тому, как устроен сам предмет. НОЖ И КРОВЬ - свои префабы в бандлах мода. Форма записи "#@modfolder(...)?prefab" подтверждена живым примером; геометрия у обоих ванильная, меняются материал и текстура. Текстуры генерируются скриптами (см. _private/tools), а не рисуются: правка вида сводится к правке констант и одному запуску. Нож: состаренная кость, почти чёрная обмотка, пурпур во впадинах, плюс слой под ручную роспись рун - он подмешивается в альбедо и в эмиссию, поэтому руны светятся. Слой пользовательский, генератор его никогда не перезаписывает. Положение росписи посчитано по геометрии: развёртка выгружена из меша, и подобрано смещение, при котором вся роспись ложится на одну плоскую грань. Раньше она перегибалась через кромку, обращённую к игроку. Кровь: банка вместо мешка. Заодно чинится расхождение, жившее с самого начала - описание предмета говорило "Банка, наполненная кровью", а наследуемый medicalBloodBag показывал sackPrefab, обычный мешок. Жидкость перерисована по маскам мешей, а не перекрашена тинтом: тинт предмета на этот меш не действует вовсе, у шейдера Game_EntityTintMaskSSS выигрывает _Color материала. ПИРАМИДА - своя краска в атласе блоков через собственный Harmony-патч. Ваниль своих текстур блоков не умеет: Texture у блока это индекс в готовом атласе, а запись краски несёт только TextureId/PaintCost/Group/SortIndex и никогда путь к картинке. Поэтому CustomBlockPaintPatch дописывает наш слой в массивы атласа на лету. Путь через свою модель (Shape="ModelEntity") пробовался и отложен: там остались нерешёнными столкновения и маджента на дальних экземплярах. Краска лучше тем, что блок остаётся обычным Shape="New" - со всеми работающими столкновениями, наведением по E и правильной посадкой, - а поверхность у него своя. Побочно краска доступна кисточкой под именем "Некротический прах". Параметры атласа не угаданы, а замерены в игре (BlockAtlasProbePatch): 512x512, DXT1 для альбедо и DXT5 для нормалей со specular, 10 мип-уровней, массивы нечитаемые - отсюда GPU-копирование и явные настройки импорта текстур. Зонд оставлен намеренно: номер краски в blocks.xml (608) это длина ванильного uvMapping, и после обновления игры он может сдвинуться - зонд печатает фактические числа при каждом запуске. Патчи обёрнуты целиком: они работают внутри загрузки игры, где вылетевшее исключение срывает шаг загрузки. Resources/necropyramid в коммит не идёт - на него никто не ссылается. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W5F3AVwsusZHMqPBcSQcVJ
52 lines
2.3 KiB
C#
52 lines
2.3 KiB
C#
using System.Reflection;
|
|
using HarmonyLib;
|
|
using UnityEngine;
|
|
|
|
namespace NecromancerTome
|
|
{
|
|
/// <summary>
|
|
/// Mod entry point. The game finds this by scanning every assembly dropped in a
|
|
/// Mods/<ModFolder>/ directory for a type implementing IModApi.
|
|
/// </summary>
|
|
public class ModEntry : IModApi
|
|
{
|
|
/// <summary>The mod's own folder, kept from InitMod so patches can find files we ship
|
|
/// (currently Resources/necroatlas for the custom block paint). Nothing else knows where
|
|
/// the mod lives - the game hands it over exactly once, right here.</summary>
|
|
public static Mod Instance;
|
|
|
|
public void InitMod(Mod _modInstance)
|
|
{
|
|
Instance = _modInstance;
|
|
var harmony = new Harmony("necromancertome.harmony");
|
|
harmony.PatchAll(Assembly.GetExecutingAssembly());
|
|
PetFollowPatch.Init();
|
|
// PyramidWardPatch.cs's TEFeaturePyramidWard needs no Init() call - it's discovered
|
|
// automatically by the engine's own TileEntityCompositeData reflection scan (see that
|
|
// file's class doc comment), not registered here like PetFollowPatch's UnityUpdate hook.
|
|
|
|
// Diagnostic-only, added 2026-08-28 while chasing "VictimPatch never logs anything at
|
|
// all for dropItemOnDeath, even though decompiling EntityAlive.OnEntityDeath()
|
|
// confirms it's called unconditionally right after the 'killed by' line seen in the
|
|
// log". This checks, at load time, whether Harmony actually attached our Prefix to
|
|
// that method at all - rules "patch never applied" in or out without waiting on
|
|
// another in-game death.
|
|
VerifyPrefixAttached(typeof(EntityAlive), "dropItemOnDeath");
|
|
VerifyPrefixAttached(typeof(Entity), "DropBagServer");
|
|
}
|
|
|
|
public static void VerifyPrefixAttached(System.Type type, string methodName)
|
|
{
|
|
MethodBase method = AccessTools.Method(type, methodName);
|
|
if (method == null)
|
|
{
|
|
Debug.LogWarning("[NecromancerTome] ModEntry: could not resolve " + type.Name + "." + methodName + " via AccessTools - method not found");
|
|
return;
|
|
}
|
|
Patches info = Harmony.GetPatchInfo(method);
|
|
int prefixCount = info != null && info.Prefixes != null ? info.Prefixes.Count : 0;
|
|
Debug.Log("[NecromancerTome] ModEntry: " + type.Name + "." + methodName + " resolved, has " + prefixCount + " prefix patch(es) attached after PatchAll");
|
|
}
|
|
}
|
|
}
|