544 lines
23 KiB
C#
544 lines
23 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Scripting;
|
||
|
||
namespace NecromancerTome
|
||
{
|
||
/// <summary>
|
||
/// Дух-работник урны - «джинн из урны» (решение 16.10): кончик хвоста уходит в горлышко, дух вырастает над урной,
|
||
/// без работы висит, чуть покачиваясь и медленно поворачиваясь. Этап 3 некротехнолога.
|
||
///
|
||
/// Не сущность игры, а видимая модель - дочерний объект модели блока урны, как каркас зоны (решение 16.10 вместо
|
||
/// плана 15.10 «сущность на префабе медсестры»): хитбокса, ИИ и сохранения у неё нет, поэтому «убить нельзя»,
|
||
/// «зомби не замечают», «сквозь стены» выходят сами, а сейвы дух не трогает - он появляется по черепу в урне
|
||
/// (TEFeatureCinerarium.HasSkull) и пропадает без него. Сдвиги Origin и выгрузку чанка переживает с моделью урны.
|
||
///
|
||
/// Модель - бандл necrotechnologist (NecroTechnologistSetup.cs): своя сетка на своём скелете NT_rig в позе покоя,
|
||
/// глаза - отдельные рендереры (в имени «eye»). Тело - светящаяся дымка (16.10: Fade давал пятна; сложение One/One,
|
||
/// холодный оттенок Tint, своё слабое свечение Glow); текстура уже серая (сделана под обесцвечивание). Глаза - свой
|
||
/// непрозрачный светящийся материал из бандла, очередь отрисовки 3100 - после тела.
|
||
/// </summary>
|
||
public static class CinerariumGenie
|
||
{
|
||
public const string ModelPath = "#@modfolder(NecromancerTome):Resources/necrotechnologist?necroTechnologistPrefab.prefab";
|
||
|
||
/// <summary>Высота корня модели над низом блока урны, м. 16.10 в Blender по модели урны: -0.15 («духа чуть приподними»).
|
||
/// Подгоняется командой necrogenie.</summary>
|
||
public static float OffsetY = -0.15f;
|
||
/// <summary>Сдвиг вперёд в системе самого духа, м (16.10: «чуть вперёд, чтобы хвост не просвечивал сквозь урну сзади»
|
||
/// - загнутый назад кончик хвоста; в Blender 0.06 - ни одной вершины за стенкой, взято 0.07). Поворачивается вместе
|
||
/// с духом, поэтому хвост всегда в горлышке. Знак проверить в игре: necrogenie fwd.</summary>
|
||
public static float Forward = 0.07f;
|
||
/// <summary>Поворот без работы, градусов в секунду.</summary>
|
||
public static float TurnSpeed = 6f;
|
||
/// <summary>Покачивание: размах (м) и период (с).</summary>
|
||
public static float BobHeight = 0.04f;
|
||
public static float BobPeriod = 4f;
|
||
public static float Alpha = 0.3f;
|
||
/// <summary>Руки (16.10: «Т-поза бесит»): плечо от вертикали вбок и вперёд, локоть согнут вперёд, градусы.
|
||
/// Модель в позе покоя, аниматора нет - кости ставятся один раз при появлении.</summary>
|
||
public static float ArmOut = 12f;
|
||
public static float ArmFwd = 15f;
|
||
public static float Elbow = 35f;
|
||
|
||
static GameObject _prefab;
|
||
static bool _tried;
|
||
static readonly Dictionary<Material, Material> _ghostMats = new Dictionary<Material, Material>();
|
||
/// <summary>Все джинны на сцене - команда necrogenie правит их сразу.</summary>
|
||
public static readonly List<CinerariumGenieMotion> Live = new List<CinerariumGenieMotion>();
|
||
|
||
static bool Load()
|
||
{
|
||
if (!_tried)
|
||
{
|
||
_tried = true;
|
||
_prefab = DataLoader.LoadAsset<GameObject>(ModelPath);
|
||
Debug.Log("[NecromancerTome] CinerariumGenie: модель " + (_prefab != null ? "загружена" : "НЕ загружена: " + ModelPath));
|
||
}
|
||
return _prefab != null;
|
||
}
|
||
|
||
public static GameObject Spawn(Transform _urnModel)
|
||
{
|
||
if (_urnModel == null || !Load())
|
||
{
|
||
return null;
|
||
}
|
||
GameObject g = Object.Instantiate(_prefab, _urnModel, false);
|
||
g.name = "NecroCinerariumGenie";
|
||
foreach (Renderer r in g.GetComponentsInChildren<Renderer>(true))
|
||
{
|
||
r.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||
r.receiveShadows = false;
|
||
if (r is SkinnedMeshRenderer smr)
|
||
{
|
||
smr.updateWhenOffscreen = true; // скелет свой и неподвижный - границы не пересчитываются, не пропадать с краю экрана
|
||
}
|
||
if (r.name.Contains("eye"))
|
||
{
|
||
continue;
|
||
}
|
||
Material[] mats = r.sharedMaterials;
|
||
for (int i = 0; i < mats.Length; i++)
|
||
{
|
||
mats[i] = Ghost(mats[i]);
|
||
}
|
||
r.sharedMaterials = mats;
|
||
}
|
||
foreach (Collider c in g.GetComponentsInChildren<Collider>(true))
|
||
{
|
||
Object.Destroy(c);
|
||
}
|
||
g.AddComponent<CinerariumGenieMotion>().Pose();
|
||
return g;
|
||
}
|
||
|
||
/// <summary>Полупрозрачная копия материала тела - одна на исходный материал на всю игру.</summary>
|
||
static Material Ghost(Material _src)
|
||
{
|
||
if (_src == null)
|
||
{
|
||
return null;
|
||
}
|
||
if (_ghostMats.TryGetValue(_src, out Material m) && m != null)
|
||
{
|
||
return m;
|
||
}
|
||
m = new Material(_src) { name = _src.name + "_ghost" };
|
||
Shader std = Shader.Find("Standard");
|
||
if (std != null && m.shader != std)
|
||
{
|
||
m.shader = std;
|
||
}
|
||
// 16.10: Fade (SrcAlpha/OneMinusSrcAlpha) давал «прозрачный, но какими-то пятнами» и «не выглядит бесплотным
|
||
// духом»: без записи глубины треугольники одной сетки ложатся в случайном порядке. Свечение (One/One) от порядка
|
||
// не зависит: дух только добавляет свет к тому, что за ним, - дымка. Альфа здесь не нужна, яркость - цветом.
|
||
m.SetFloat("_Mode", 2f);
|
||
m.SetOverrideTag("RenderType", "Transparent");
|
||
m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||
m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||
m.SetInt("_ZWrite", 0);
|
||
m.DisableKeyword("_ALPHATEST_ON");
|
||
m.DisableKeyword("_ALPHABLEND_ON");
|
||
m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||
if (m.HasProperty("_Glossiness"))
|
||
{
|
||
m.SetFloat("_Glossiness", 0f); // без бликов: блик при сложении - белое пятно
|
||
}
|
||
if (m.HasProperty("_Metallic"))
|
||
{
|
||
m.SetFloat("_Metallic", 0f);
|
||
}
|
||
if (m.HasProperty("_EmissionMap") && m.HasProperty("_MainTex"))
|
||
{
|
||
m.SetTexture("_EmissionMap", m.GetTexture("_MainTex")); // своё свечение - чтобы ночью не пропадал
|
||
m.EnableKeyword("_EMISSION");
|
||
m.globalIlluminationFlags = MaterialGlobalIlluminationFlags.None;
|
||
}
|
||
m.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
|
||
SetLook(m);
|
||
_ghostMats[_src] = m;
|
||
return m;
|
||
}
|
||
|
||
/// <summary>Холодный бледный оттенок духа (умножается на серую текстуру).</summary>
|
||
public static Color Tint = new Color(0.55f, 0.75f, 1f, 1f);
|
||
/// <summary>Своё свечение тела, доля от Tint.</summary>
|
||
public static float Glow = 0.35f;
|
||
|
||
static void SetLook(Material _m)
|
||
{
|
||
if (_m == null)
|
||
{
|
||
return;
|
||
}
|
||
if (_m.HasProperty("_Color"))
|
||
{
|
||
_m.color = new Color(Tint.r * Alpha, Tint.g * Alpha, Tint.b * Alpha, 1f);
|
||
}
|
||
if (_m.HasProperty("_EmissionColor"))
|
||
{
|
||
_m.SetColor("_EmissionColor", new Color(Tint.r * Glow, Tint.g * Glow, Tint.b * Glow, 1f));
|
||
}
|
||
}
|
||
|
||
public static void ApplyAlpha()
|
||
{
|
||
foreach (Material m in _ghostMats.Values)
|
||
{
|
||
SetLook(m);
|
||
}
|
||
}
|
||
|
||
static readonly Dictionary<Material, Material> _stuckMats = new Dictionary<Material, Material>();
|
||
|
||
/// <summary>Почерневший дух (класть некуда): свечением чёрное не нарисовать, поэтому обычная Fade-копия, тёмная.</summary>
|
||
public static Material Stuck(Material _src)
|
||
{
|
||
if (_src == null)
|
||
{
|
||
return null;
|
||
}
|
||
if (_stuckMats.TryGetValue(_src, out Material m) && m != null)
|
||
{
|
||
return m;
|
||
}
|
||
m = new Material(_src) { name = _src.name + "_stuck" };
|
||
Shader std = Shader.Find("Standard");
|
||
if (std != null && m.shader != std)
|
||
{
|
||
m.shader = std;
|
||
}
|
||
m.SetFloat("_Mode", 2f);
|
||
m.SetOverrideTag("RenderType", "Transparent");
|
||
m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
||
m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
||
m.SetInt("_ZWrite", 1); // с глубиной: пятен нет, тёмный и почти плотный
|
||
m.DisableKeyword("_ALPHATEST_ON");
|
||
m.EnableKeyword("_ALPHABLEND_ON");
|
||
m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||
m.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
|
||
if (m.HasProperty("_Color"))
|
||
{
|
||
m.color = new Color(0.04f, 0.035f, 0.045f, 0.8f);
|
||
}
|
||
_stuckMats[_src] = m;
|
||
return m;
|
||
}
|
||
|
||
/// <summary>Исходный материал тела по его светящейся копии.</summary>
|
||
public static Material Source(Material _ghost)
|
||
{
|
||
foreach (KeyValuePair<Material, Material> p in _ghostMats)
|
||
{
|
||
if (p.Value == _ghost)
|
||
{
|
||
return p.Key;
|
||
}
|
||
}
|
||
return _ghost;
|
||
}
|
||
}
|
||
|
||
/// <summary>Покачивание и поворот джинна над урной. Только вид - никакой логики.</summary>
|
||
[Preserve]
|
||
public class CinerariumGenieMotion : MonoBehaviour
|
||
{
|
||
float _yaw;
|
||
float _phase;
|
||
/// <summary>Полёт к месту работы и обратно (16.10: «возвращается задом», «делал действие не сразу, а хоть руками
|
||
/// пошевелив»): туда - лицом к месту, над местом - жест руками (дело делается в его середине, см. ActDelay),
|
||
/// обратно - развернувшись лицом к урне. Точка - в координатах модели урны; _t - время с начала полёта, -1 - дома.</summary>
|
||
Vector3 _visit;
|
||
float _t = -1f;
|
||
public static float OutSeconds = 1.3f;
|
||
public static float WorkSeconds = 1.6f;
|
||
public static float BackSeconds = 1.3f;
|
||
/// <summary>Через сколько после начала полёта дело выполняется - в середине жеста.</summary>
|
||
public static float ActDelay => OutSeconds + WorkSeconds * 0.5f;
|
||
/// <summary>Жест: плечи вперёд до GestureFwd градусов, локти ходят на ±GestureWave вразнобой.</summary>
|
||
public static float GestureFwd = 55f;
|
||
public static float GestureWave = 30f;
|
||
bool _gesturing;
|
||
/// <summary>Почернел и замер (готовое класть некуда, решение 06.10).</summary>
|
||
bool _stuck;
|
||
/// <summary>Кости рук (плечо, предплечье, кисть по сторонам) и их поворот в позе покоя.</summary>
|
||
Transform[] _arm;
|
||
Quaternion[] _armRest;
|
||
static bool _armLogged;
|
||
|
||
/// <summary>Опустить руки из позы покоя: направление плеча и предплечья задаётся в осях модели (вперёд - +Z),
|
||
/// поворот - от текущего направления кости к нужному, так что оси самих костей значения не имеют.</summary>
|
||
public void Pose()
|
||
{
|
||
Pose(CinerariumGenie.ArmFwd, CinerariumGenie.Elbow, CinerariumGenie.Elbow);
|
||
}
|
||
|
||
void Pose(float _fwd, float _elbowL, float _elbowR)
|
||
{
|
||
if (_arm == null)
|
||
{
|
||
string[] names = { "LeftArm", "LeftForeArm", "LeftHand", "RightArm", "RightForeArm", "RightHand" };
|
||
_arm = new Transform[names.Length];
|
||
_armRest = new Quaternion[names.Length];
|
||
foreach (Transform t in GetComponentsInChildren<Transform>(true))
|
||
{
|
||
for (int i = 0; i < names.Length; i++)
|
||
{
|
||
if (_arm[i] == null && t.name.EndsWith(names[i]))
|
||
{
|
||
_arm[i] = t;
|
||
_armRest[i] = t.localRotation;
|
||
}
|
||
}
|
||
}
|
||
if (!_armLogged)
|
||
{
|
||
_armLogged = true;
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||
for (int i = 0; i < names.Length; i++)
|
||
{
|
||
sb.Append(names[i]).Append('=').Append(_arm[i] != null ? "ok" : "НЕТ").Append(' ');
|
||
}
|
||
Debug.Log("[NecromancerTome] CinerariumGenie: кости рук " + sb);
|
||
}
|
||
}
|
||
for (int i = 0; i < _arm.Length; i++)
|
||
{
|
||
if (_arm[i] != null)
|
||
{
|
||
_arm[i].localRotation = _armRest[i];
|
||
}
|
||
}
|
||
for (int side = 0; side < 2; side++)
|
||
{
|
||
Transform up = _arm[side * 3], fore = _arm[side * 3 + 1], hand = _arm[side * 3 + 2];
|
||
if (up == null || fore == null || hand == null)
|
||
{
|
||
continue;
|
||
}
|
||
float sign = Mathf.Sign(transform.InverseTransformPoint(up.position).x);
|
||
float o = CinerariumGenie.ArmOut * Mathf.Deg2Rad, f = _fwd * Mathf.Deg2Rad;
|
||
Vector3 upDir = new Vector3(sign * Mathf.Sin(o), -Mathf.Cos(o) * Mathf.Cos(f), Mathf.Sin(f)).normalized;
|
||
Vector3 foreDir = Vector3.RotateTowards(upDir, Vector3.forward, (side == 0 ? _elbowL : _elbowR) * Mathf.Deg2Rad, 0f);
|
||
up.rotation = Quaternion.FromToRotation(fore.position - up.position, transform.TransformDirection(upDir)) * up.rotation;
|
||
fore.rotation = Quaternion.FromToRotation(hand.position - fore.position, transform.TransformDirection(foreDir)) * fore.rotation;
|
||
}
|
||
}
|
||
|
||
/// <summary>Светящиеся материалы рендереров - вернуть после почернения.</summary>
|
||
readonly Dictionary<Renderer, Material[]> _glowing = new Dictionary<Renderer, Material[]>();
|
||
|
||
/// <summary>Куда смотреть за работой (в координатах модели урны); _beside - дух стоит сбоку от места, а не над ним.</summary>
|
||
Vector3 _look;
|
||
bool _beside;
|
||
|
||
/// <summary>Точки - в координатах сцены (без Origin): _stand - где встать (низ клетки), _look - низ клетки места работы.</summary>
|
||
public void Visit(Vector3 _stand, Vector3 _lookAt, bool _besideIt)
|
||
{
|
||
if (transform.parent == null)
|
||
{
|
||
return;
|
||
}
|
||
_visit = transform.parent.InverseTransformPoint(_stand);
|
||
_look = transform.parent.InverseTransformPoint(_lookAt);
|
||
_beside = _besideIt;
|
||
_t = 0f;
|
||
}
|
||
|
||
public void SetStuck(bool _on)
|
||
{
|
||
if (_stuck == _on)
|
||
{
|
||
return;
|
||
}
|
||
_stuck = _on;
|
||
foreach (Renderer r in GetComponentsInChildren<Renderer>(true))
|
||
{
|
||
if (r.name.Contains("eye"))
|
||
{
|
||
continue;
|
||
}
|
||
if (!_glowing.ContainsKey(r))
|
||
{
|
||
_glowing[r] = r.sharedMaterials;
|
||
}
|
||
Material[] glow = _glowing[r];
|
||
if (_on)
|
||
{
|
||
Material[] dark = new Material[glow.Length];
|
||
for (int i = 0; i < glow.Length; i++)
|
||
{
|
||
dark[i] = CinerariumGenie.Stuck(CinerariumGenie.Source(glow[i]));
|
||
}
|
||
r.sharedMaterials = dark;
|
||
}
|
||
else
|
||
{
|
||
r.sharedMaterials = glow;
|
||
}
|
||
}
|
||
}
|
||
|
||
void OnEnable()
|
||
{
|
||
CinerariumGenie.Live.Add(this);
|
||
_phase = Random.value * 10f;
|
||
_yaw = Random.value * 360f;
|
||
}
|
||
|
||
void OnDisable()
|
||
{
|
||
CinerariumGenie.Live.Remove(this);
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
if (_stuck)
|
||
{
|
||
return; // замер как был
|
||
}
|
||
float yawTarget = _yaw + CinerariumGenie.TurnSpeed * Time.deltaTime;
|
||
Vector3 home = new Vector3(0f, CinerariumGenie.OffsetY, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
|
||
if (_t >= 0f)
|
||
{
|
||
_t += Time.deltaTime;
|
||
if (_t < OutSeconds + WorkSeconds + BackSeconds)
|
||
{
|
||
// Сбоку - на уровне места (тело вровень со станцией), над местом - выше него.
|
||
Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + (_beside ? 0.15f : 0.8f), 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
|
||
Vector3 d = _visit - new Vector3(0f, CinerariumGenie.OffsetY, 0f);
|
||
bool far = d.x * d.x + d.z * d.z > 0.01f;
|
||
float toPlace = far ? Mathf.Atan2(d.x, d.z) * Mathf.Rad2Deg : _yaw;
|
||
Vector3 dl = _look - _visit;
|
||
float toLook = dl.x * dl.x + dl.z * dl.z > 0.01f ? Mathf.Atan2(dl.x, dl.z) * Mathf.Rad2Deg : toPlace;
|
||
float k, face, w = 0f;
|
||
if (_t < OutSeconds)
|
||
{
|
||
k = Mathf.SmoothStep(0f, 1f, _t / OutSeconds);
|
||
face = toPlace;
|
||
}
|
||
else if (_t < OutSeconds + WorkSeconds)
|
||
{
|
||
k = 1f;
|
||
face = toLook; // за работой - лицом к станции
|
||
w = Mathf.Sin((_t - OutSeconds) / WorkSeconds * Mathf.PI);
|
||
}
|
||
else
|
||
{
|
||
k = 1f - Mathf.SmoothStep(0f, 1f, (_t - OutSeconds - WorkSeconds) / BackSeconds);
|
||
face = far ? toPlace + 180f : _yaw; // назад - лицом к урне
|
||
}
|
||
_yaw = Mathf.MoveTowardsAngle(_yaw, face, 300f * Time.deltaTime) % 360f;
|
||
transform.localPosition = Vector3.Lerp(home, over, k);
|
||
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
|
||
if (w > 0f)
|
||
{
|
||
float wave = Mathf.Sin(_t * Mathf.PI * 2f * 1.5f) * GestureWave * w;
|
||
Pose(Mathf.Lerp(CinerariumGenie.ArmFwd, GestureFwd, w), CinerariumGenie.Elbow + 15f * w + wave, CinerariumGenie.Elbow + 15f * w - wave);
|
||
_gesturing = true;
|
||
}
|
||
else if (_gesturing)
|
||
{
|
||
Pose();
|
||
_gesturing = false;
|
||
}
|
||
return;
|
||
}
|
||
_t = -1f;
|
||
if (_gesturing)
|
||
{
|
||
Pose();
|
||
_gesturing = false;
|
||
}
|
||
}
|
||
_yaw = yawTarget % 360f;
|
||
float bob = CinerariumGenie.BobPeriod > 0.01f
|
||
? Mathf.Sin((Time.time + _phase) * Mathf.PI * 2f / CinerariumGenie.BobPeriod) * CinerariumGenie.BobHeight
|
||
: 0f;
|
||
transform.localPosition = home + new Vector3(0f, bob, 0f);
|
||
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
|
||
}
|
||
}
|
||
|
||
/// <summary>Подгонка джинна в игре: высота над урной, скорость поворота, покачивание, прозрачность. Не сохраняется -
|
||
/// найденные значения переносятся в константы CinerariumGenie.</summary>
|
||
[Preserve]
|
||
public class ConsoleCmdNecroGenie : ConsoleCmdAbstract
|
||
{
|
||
public override bool IsExecuteOnClient => true;
|
||
|
||
public override bool AllowedInMainMenu => false;
|
||
|
||
public override string[] getCommands()
|
||
{
|
||
return new string[] { "necrogenie" };
|
||
}
|
||
|
||
public override string getDescription()
|
||
{
|
||
return "Necromancer's Tome: tune the cinerarium spirit (height over the urn, turning, bobbing, transparency).";
|
||
}
|
||
|
||
public override string getHelp()
|
||
{
|
||
return "necrogenie - show the current values and how many spirits are on screen\n" +
|
||
"necrogenie y <m> - model root height above the urn block's bottom (tail tip is 0.47 m above the root)\n" +
|
||
"necrogenie fwd <m> - shift along the spirit's own forward (keeps the curled tail inside the neck)\n" +
|
||
"necrogenie turn <deg/s> - idle turning speed\n" +
|
||
"necrogenie bob <m> <s> - bobbing height and period\n" +
|
||
"necrogenie alpha <0-1> - body brightness (the body adds light, it is not alpha-blended)\n" +
|
||
"necrogenie glow <0-1> - body's own glow (visible at night)\n" +
|
||
"necrogenie tint <r> <g> <b> - body colour\n" +
|
||
"necrogenie arms <out> <fwd> <elbow> - arm pose, degrees: upper arm from vertical sideways and forward, elbow bend";
|
||
}
|
||
|
||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
|
||
{
|
||
SdtdConsole con = SingletonMonoBehaviour<SdtdConsole>.Instance;
|
||
if (_params.Count >= 2 && float.TryParse(_params[1], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float v))
|
||
{
|
||
switch (_params[0].ToLowerInvariant())
|
||
{
|
||
case "y":
|
||
CinerariumGenie.OffsetY = v;
|
||
break;
|
||
case "fwd":
|
||
CinerariumGenie.Forward = v;
|
||
break;
|
||
case "turn":
|
||
CinerariumGenie.TurnSpeed = v;
|
||
break;
|
||
case "bob":
|
||
CinerariumGenie.BobHeight = v;
|
||
if (_params.Count >= 3 && float.TryParse(_params[2], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float per))
|
||
{
|
||
CinerariumGenie.BobPeriod = per;
|
||
}
|
||
break;
|
||
case "alpha":
|
||
CinerariumGenie.Alpha = Mathf.Clamp01(v);
|
||
CinerariumGenie.ApplyAlpha();
|
||
break;
|
||
case "glow":
|
||
CinerariumGenie.Glow = Mathf.Clamp01(v);
|
||
CinerariumGenie.ApplyAlpha();
|
||
break;
|
||
case "arms":
|
||
CinerariumGenie.ArmOut = v;
|
||
if (_params.Count >= 3 && float.TryParse(_params[2], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float af))
|
||
{
|
||
CinerariumGenie.ArmFwd = af;
|
||
}
|
||
if (_params.Count >= 4 && float.TryParse(_params[3], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float ae))
|
||
{
|
||
CinerariumGenie.Elbow = ae;
|
||
}
|
||
foreach (CinerariumGenieMotion m in CinerariumGenie.Live)
|
||
{
|
||
m.Pose();
|
||
}
|
||
break;
|
||
case "tint":
|
||
if (_params.Count >= 4 &&
|
||
float.TryParse(_params[2], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float g) &&
|
||
float.TryParse(_params[3], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float b))
|
||
{
|
||
CinerariumGenie.Tint = new Color(v, g, b, 1f);
|
||
CinerariumGenie.ApplyAlpha();
|
||
}
|
||
break;
|
||
default:
|
||
con.Output("necrogenie: unknown '" + _params[0] + "' - see 'help necrogenie'");
|
||
return;
|
||
}
|
||
}
|
||
con.Output("necrogenie: y " + CinerariumGenie.OffsetY.ToString("F2") + ", fwd " + CinerariumGenie.Forward.ToString("F2") + ", turn " + CinerariumGenie.TurnSpeed.ToString("F1") +
|
||
", bob " + CinerariumGenie.BobHeight.ToString("F2") + " / " + CinerariumGenie.BobPeriod.ToString("F1") + " s, alpha " +
|
||
CinerariumGenie.Alpha.ToString("F2") + ", glow " + CinerariumGenie.Glow.ToString("F2") + ", tint " +
|
||
CinerariumGenie.Tint.r.ToString("F2") + " " + CinerariumGenie.Tint.g.ToString("F2") + " " + CinerariumGenie.Tint.b.ToString("F2") + ", arms " + CinerariumGenie.ArmOut.ToString("F0") + " " +
|
||
CinerariumGenie.ArmFwd.ToString("F0") + " " + CinerariumGenie.Elbow.ToString("F0") + "; spirits on screen: " + CinerariumGenie.Live.Count);
|
||
}
|
||
}
|
||
}
|