Режим змеи для Ножа некроманта и чёрный разрыв
Ночью по R с ножом - дух змеи: тело падает (-95% HP), игрок управляет призрачной змеёй от первого лица. Свободный ход по миру (свой наблюдатель чанков, отрисовка, звук, спавн и спящие считаются от змеи), прыжок, атака на ЛКМ - рывок сквозь препятствия и чёрный разрыв, ПКМ - выбраться из текстур. Тёмное чутьё, тревога у тела, отсчёт в радиации края карты, пурпурный фильтр зрения (necrovision). Утром змея развеивается. Чёрный разрыв (NecroBurst.cs): клон частиц confetti в чёрном, команда necroburst. Проверено в игре 28.09. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4cdd315b98
commit
6ff76d174e
@@ -318,6 +318,11 @@ namespace NecromancerTome
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Призрачная змея режима змеи (SnakeMode.cs, 2026-09-28): «текстуры как у торговцев».
|
||||
if (entityClass.entityClassName == SnakeMode.SnakeClassName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return Patch_ItemActionSpawnEntity_Spawn_PetLimit.LimitedPets.ContainsKey(entityClass.entityClassName);
|
||||
}
|
||||
|
||||
@@ -429,7 +434,15 @@ namespace NecromancerTome
|
||||
|
||||
// Before anything is touched, because the body's replacement shader is found on the
|
||||
// trader's OWN hair and the hair is not guaranteed to come first in this array.
|
||||
// Змея не должна становиться источником BlendShader: он кэшируется на всю сессию, и
|
||||
// торговцы после неё перекрашивались бы шейдером змеи, а не своих волос.
|
||||
bool isSnake = _trader.EntityClass.entityClassName == SnakeMode.SnakeClassName;
|
||||
Shader keptBlendShader = BlendShader;
|
||||
FindBlendShader(renderers);
|
||||
if (isSnake)
|
||||
{
|
||||
BlendShader = keptBlendShader;
|
||||
}
|
||||
|
||||
int converted = 0;
|
||||
int leversBefore = TintedMaterials.Count;
|
||||
@@ -456,6 +469,17 @@ namespace NecromancerTome
|
||||
}
|
||||
_body.Renderers = taken.ToArray();
|
||||
|
||||
// Змея режима змеи идёт по ветке «блендим на месте» (Autodesk Interactive), и альфа там
|
||||
// не видна даже с полной шестёркой настроек (_Mode 2, _ZWrite 0, ключевые слова, очередь)
|
||||
// - проверено 28.09 дважды. Вероятная причина: копия шейдера из сборки змеи собрана без
|
||||
// варианта _ALPHABLEND_ON (Unity выкидывает неиспользуемые варианты при сборке). Поэтому
|
||||
// материалы змеи пересобираются на шейдер, который этот вариант ТОЧНО имеет - его берём
|
||||
// у любого загруженного материала с включённым _ALPHABLEND_ON.
|
||||
if (isSnake)
|
||||
{
|
||||
MakeSnakeFade(taken);
|
||||
}
|
||||
|
||||
// The lever count is the half that answers "will the console command reach him":
|
||||
// desaturation and opacity come from different properties, and the body had the first
|
||||
// without the second until 2026-09-14. Fewer levers than converted renderers means
|
||||
@@ -466,6 +490,95 @@ namespace NecromancerTome
|
||||
return true;
|
||||
}
|
||||
|
||||
static Shader _fadeShader;
|
||||
|
||||
/// <summary>Шейдер, у которого точно есть прозрачный вариант: берётся с любого загруженного
|
||||
/// материала, где _ALPHABLEND_ON включён. Предпочтение - тем же шейдерам, что у змеи.</summary>
|
||||
public static Shader FindFadeShader()
|
||||
{
|
||||
if (_fadeShader != null)
|
||||
{
|
||||
return _fadeShader;
|
||||
}
|
||||
Shader best = null;
|
||||
int bestRank = int.MaxValue;
|
||||
HashSet<string> seen = new HashSet<string>();
|
||||
foreach (Material m in Resources.FindObjectsOfTypeAll<Material>())
|
||||
{
|
||||
if (m == null || m.shader == null || !m.IsKeywordEnabled("_ALPHABLEND_ON") ||
|
||||
!m.HasProperty("_MainTex") || !m.HasProperty("_Color") || !m.HasProperty("_SrcBlend"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string name = m.shader.name;
|
||||
seen.Add(name + " (" + m.name + ")");
|
||||
int rank = name == "Autodesk Interactive" ? 0 : name == "Standard" ? 1 : 2;
|
||||
if (rank < bestRank)
|
||||
{
|
||||
best = m.shader;
|
||||
bestRank = rank;
|
||||
}
|
||||
}
|
||||
Debug.Log("[NecromancerTome] GhostTraderPatch: fade shaders loaded: " +
|
||||
(seen.Count == 0 ? "<none>" : string.Join(", ", seen)) +
|
||||
" -> " + (best != null ? best.name : "NONE"));
|
||||
_fadeShader = best;
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>Пересобирает материалы змеи на шейдер из FindFadeShader: серая текстура и карта
|
||||
/// нормалей переносятся, прозрачность - полной шестёркой.</summary>
|
||||
public static void MakeSnakeFade(List<Renderer> _renderers)
|
||||
{
|
||||
Shader fade = FindFadeShader();
|
||||
if (fade == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int done = 0;
|
||||
foreach (Renderer renderer in _renderers)
|
||||
{
|
||||
Material[] current = renderer.sharedMaterials;
|
||||
Material[] rebuilt = new Material[current.Length];
|
||||
for (int i = 0; i < current.Length; i++)
|
||||
{
|
||||
Material src = current[i];
|
||||
if (src == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Material m = new Material(fade);
|
||||
string albedo = FindAlbedoProperty(src);
|
||||
if (albedo != null)
|
||||
{
|
||||
m.SetTexture("_MainTex", src.GetTexture(albedo));
|
||||
}
|
||||
Texture normal = FindTexture(src, NormalNameHints);
|
||||
if (normal != null && m.HasProperty("_BumpMap"))
|
||||
{
|
||||
m.SetTexture("_BumpMap", normal);
|
||||
m.EnableKeyword("_NORMALMAP");
|
||||
}
|
||||
MakeMatte(m);
|
||||
if (m.HasProperty("_Mode"))
|
||||
{
|
||||
m.SetFloat("_Mode", 2f);
|
||||
}
|
||||
if (m.HasProperty("_ZWrite"))
|
||||
{
|
||||
m.SetFloat("_ZWrite", 0f);
|
||||
}
|
||||
m.DisableKeyword("_ALPHATEST_ON");
|
||||
m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
ApplyTransparency(m);
|
||||
rebuilt[i] = m;
|
||||
done++;
|
||||
}
|
||||
renderer.materials = rebuilt;
|
||||
}
|
||||
Debug.Log("[NecromancerTome] GhostTraderPatch: snake re-shaded onto '" + fade.name + "', " + done + " material(s)");
|
||||
}
|
||||
|
||||
/// <summary>Builds and installs this renderer's ghost materials from the ORIGINALS it was
|
||||
/// handed. True when anything was actually changed - a renderer nothing could be done to
|
||||
/// keeps the materials it has rather than being handed a half-built array.</summary>
|
||||
|
||||
Reference in New Issue
Block a user