Цинерарий-старатель (#64): дух добывает ископаемые
Урна necroCinerariumProspector (и ...Skull): в «Содержимом» череп и образец - один из девяти ресурсов (камень, глина, песок, древесина, железо, свинец, уголь, селитра, сланец), нить к ящику. Дух отлетает на соседнюю свободную клетку и уходит на 5 блоков под землю, растворяясь; раз в игровой час поднимается так же, кладёт добычу в ящик и уходит снова. За сутки: камень/глина/песок/древесина 30000, руды 18000, ходка ±20%. Из руды - шанс на алмаз, серебро, золото по шансам игры на каждый «блок» руды. Ящик полон - дух ждёт с добычей. Без зоны и вне лимита урн. Рецепт - кузня: латунь 40, прах 100, кости 10, кровь/сок, железная кирка; открывается с остальными урнами. Консоль necroprospector: on/off, trip, yield, spread, depth, состояние. Тексты - 13 языков; глава книги «Цинерарии» - лист Fig. VIII с шестой урной и фраза про prospektisto. Проверено в игре (test8): ходка железа 625 в ящике, спуск и подъём духа, лист и страница книги помещаются. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f1010f98b7
commit
0999fd3afc
@@ -0,0 +1,650 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// Цинерарий-старатель (#64, решения 20.10 и 22.10.2026): дух добывает ископаемые. В «Содержимом» две ячейки, как у рабочего:
|
||||
/// череп и образец - что добывать (один из Ores). Нить к ящику (любая, как у лутера) - куда класть. Есть всё - дух отлетает на
|
||||
/// ближайшую свободную клетку рядом с урной и уходит на ProspectorDepth блоков вниз, растворяясь; раз в игровой час поднимается
|
||||
/// так же, кладёт добычу в ящик и уходит снова. Без зоны и вне лимита урн, как остальные некротехнологи.
|
||||
///
|
||||
/// Добыча (20.10: «цена - скоростью, а не ресурсом»): за игровые сутки камень, глина, песок, древесина - 5 стопок по 6000,
|
||||
/// железо, свинец, уголь, селитра, сланец - 3 стопки; ходка - сутки / 24, разброс ±20%. Находки (20.10: «шанс добыть алмаз,
|
||||
/// серебро и золото, как и в игре»; 22.10 - всегда, без книги «Удачный удар», латуни нет): ходка руды = столько блоков, сколько
|
||||
/// даёт её выход с блока в игре (железо 105, прочие 50), на каждый - шансы игры (алмаз 0.5%, серебро 1%, золото 0.7%).
|
||||
///
|
||||
/// Время - игровое (worldTime), чтобы добыча за сутки не зависела от их длины. Ходка засчитывается, только пока чанк урны
|
||||
/// загружен; время перескочило больше чем на час (сон, прилив, settime) - ходка не засчитана, отсчёт заново. Ящик полон или
|
||||
/// открыт игроком - дух ждёт дома с добычей (почернел, если полон) и докладывает, как появится место; новой ходки нет.
|
||||
/// Добыча в руках духа не сохраняется: урну подняли или чанк выгрузился - ходка пропала.
|
||||
/// </summary>
|
||||
public partial class TEFeatureCinerarium
|
||||
{
|
||||
public const string ProspectorBlockName = "necroCinerariumProspector";
|
||||
public const string ProspectorSkullBlockName = "necroCinerariumProspectorSkull";
|
||||
|
||||
public static bool IsProspectorBlock(Block _b)
|
||||
{
|
||||
string n = _b?.GetBlockName();
|
||||
return n == ProspectorBlockName || n == ProspectorSkullBlockName;
|
||||
}
|
||||
|
||||
bool _prospector;
|
||||
|
||||
public bool IsProspector
|
||||
{
|
||||
get
|
||||
{
|
||||
_ = IsLogist; // узнаёт тип блока
|
||||
return _prospector;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Что добывает старатель: предмет, добыча за игровые сутки, выход с блока руды в игре (0 - находок нет).</summary>
|
||||
public static readonly (string item, int perDay, int perBlock)[] Ores =
|
||||
{
|
||||
("resourceRockSmall", 30000, 0),
|
||||
("resourceClayLump", 30000, 0),
|
||||
("resourceCrushedSand", 30000, 0),
|
||||
("resourceWood", 30000, 0),
|
||||
("resourceScrapIron", 18000, 105),
|
||||
("resourceScrapLead", 18000, 50),
|
||||
("resourceCoal", 18000, 50),
|
||||
("resourcePotassiumNitratePowder", 18000, 50),
|
||||
("resourceOilShale", 18000, 50),
|
||||
};
|
||||
|
||||
/// <summary>Находки с блока руды - как в blocks.xml игры (тег artOfMiningLuckyStrikeHarvest).</summary>
|
||||
public static readonly (string item, float prob)[] Finds =
|
||||
{
|
||||
("resourceRawDiamond", 0.005f),
|
||||
("resourceSilverNugget", 0.01f),
|
||||
("resourceGoldNugget", 0.007f),
|
||||
};
|
||||
|
||||
/// <summary>Выключатель (консоль necroprospector off): духи остаются дома, ходок нет.</summary>
|
||||
public static bool ProspectorOn = true;
|
||||
/// <summary>Множитель добычи за ходку (консоль, для проверки баланса).</summary>
|
||||
public static float ProspectorYield = 1f;
|
||||
public static float ProspectorSpread = 0.2f;
|
||||
/// <summary>На сколько блоков дух уходит вниз.</summary>
|
||||
public static int ProspectorDepth = 5;
|
||||
/// <summary>Ходка - игровой час (1000 единиц worldTime).</summary>
|
||||
public const ulong TripTicks = 1000UL;
|
||||
/// <summary>necroprospector trip - номер «внеочередной ходки»: урна, ещё не видевшая его, ходит сразу.</summary>
|
||||
public static int ForceTrip;
|
||||
|
||||
public enum ProspectorStatus
|
||||
{
|
||||
NoSkull,
|
||||
Tide,
|
||||
Off,
|
||||
NoSample,
|
||||
NoChest,
|
||||
Full,
|
||||
Digging
|
||||
}
|
||||
|
||||
bool _under;
|
||||
ulong _tripAt;
|
||||
float _nextProspect;
|
||||
float _deliverAt;
|
||||
int _forceSeen;
|
||||
readonly List<ItemStack> _carry = new List<ItemStack>();
|
||||
|
||||
public static int OreIndex(ItemValue _iv)
|
||||
{
|
||||
string n = _iv?.ItemClass?.GetItemName();
|
||||
if (n == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < Ores.Length; i++)
|
||||
{
|
||||
if (Ores[i].item == n)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static bool IsOreSample(ItemStack _s) => _s != null && !_s.IsEmpty() && OreIndex(_s.itemValue) >= 0;
|
||||
|
||||
/// <summary>Сколько игровых минут до конца ходки (для консоли), -1 - дух не под землёй.</summary>
|
||||
public int TripMinutesLeft(World _w)
|
||||
{
|
||||
if (!_under || _w == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return _tripAt > _w.worldTime ? (int)((_tripAt - _w.worldTime) * 60UL / TripTicks) : 0;
|
||||
}
|
||||
|
||||
public int CarryCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int n = 0;
|
||||
foreach (ItemStack s in _carry)
|
||||
{
|
||||
n += s.count;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public ProspectorStatus ProspectorNow(World _world)
|
||||
{
|
||||
if (!HasSkull)
|
||||
{
|
||||
return ProspectorStatus.NoSkull;
|
||||
}
|
||||
if (CinerariumLoad.Tide)
|
||||
{
|
||||
return ProspectorStatus.Tide;
|
||||
}
|
||||
if (!ProspectorOn)
|
||||
{
|
||||
return ProspectorStatus.Off;
|
||||
}
|
||||
if (!IsOreSample(Sample))
|
||||
{
|
||||
return ProspectorStatus.NoSample;
|
||||
}
|
||||
if (!HasThreads)
|
||||
{
|
||||
return ProspectorStatus.NoChest;
|
||||
}
|
||||
return Stuck ? ProspectorStatus.Full : ProspectorStatus.Digging;
|
||||
}
|
||||
|
||||
string ProspectorStateText(World _world)
|
||||
{
|
||||
string key = ProspectorNow(_world) switch
|
||||
{
|
||||
ProspectorStatus.Tide => "necroZoneUrnTide",
|
||||
ProspectorStatus.NoSample => "necroProspectorNoSample",
|
||||
ProspectorStatus.NoChest => "necroProspectorNoChest",
|
||||
ProspectorStatus.Full => "necroZoneUrnFull",
|
||||
ProspectorStatus.Digging => "necroProspectorDigging",
|
||||
_ => null
|
||||
};
|
||||
return key != null ? ", " + Localization.Get(key) : "";
|
||||
}
|
||||
|
||||
/// <summary>Работа старателя (сервер, раз в секунду): донести добычу, уйти под землю, ходка по часам игры.</summary>
|
||||
void ProspectorWork(World _world)
|
||||
{
|
||||
if (Time.time < _nextProspect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_nextProspect = Time.time + 1f;
|
||||
CinerariumGenieMotion motion = _genie != null ? _genie.GetComponent<CinerariumGenieMotion>() : null;
|
||||
ulong now = _world.worldTime;
|
||||
if (_carry.Count > 0)
|
||||
{
|
||||
if (Time.time < _deliverAt)
|
||||
{
|
||||
return; // дух ещё поднимается
|
||||
}
|
||||
Deliver(_world);
|
||||
bool full = _carry.Count > 0;
|
||||
if (full != Stuck)
|
||||
{
|
||||
Stuck = full;
|
||||
Debug.Log("[NecromancerTome] Cinerarium: старатель " + ToWorldPos() + (full ? " - ящик полон, дух ждёт с добычей" : " - добыча в ящике"));
|
||||
}
|
||||
motion?.SetStuck(full);
|
||||
if (full)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
ProspectorStatus st = ProspectorNow(_world);
|
||||
if (st != ProspectorStatus.Digging)
|
||||
{
|
||||
if (_under)
|
||||
{
|
||||
_under = false;
|
||||
motion?.Rise();
|
||||
Debug.Log("[NecromancerTome] Cinerarium: дух старателя " + ToWorldPos() + " вернулся без добычи - " + st);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!_under)
|
||||
{
|
||||
_under = true;
|
||||
if (_tripAt <= now)
|
||||
{
|
||||
_tripAt = now + TripTicks;
|
||||
}
|
||||
_forceSeen = ForceTrip;
|
||||
motion?.Dive(DigStand(_world) - Origin.position, ProspectorDepth);
|
||||
Debug.Log("[NecromancerTome] Cinerarium: дух старателя " + ToWorldPos() + " ушёл под землю за " + Sample.itemValue.ItemClass?.GetItemName());
|
||||
return;
|
||||
}
|
||||
if (motion != null && !motion.Digging)
|
||||
{
|
||||
motion.Bury(DigStand(_world) - Origin.position, ProspectorDepth); // дух появился заново (чанк, прилив) - сразу под землёй
|
||||
}
|
||||
bool forced = _forceSeen != ForceTrip;
|
||||
if (now < _tripAt && !forced)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_forceSeen = ForceTrip;
|
||||
if (!forced && now > _tripAt + TripTicks)
|
||||
{
|
||||
_tripAt = now + TripTicks; // время перескочило - ходка не засчитана
|
||||
Debug.Log("[NecromancerTome] Cinerarium: у старателя " + ToWorldPos() + " время перескочило - ходка заново");
|
||||
return;
|
||||
}
|
||||
DigTrip(OreIndex(Sample.itemValue));
|
||||
_tripAt = forced ? now + TripTicks : _tripAt + TripTicks;
|
||||
_under = false; // поднимается; донесёт - уйдёт снова
|
||||
_deliverAt = Time.time + (motion != null ? motion.Rise() : 0f);
|
||||
}
|
||||
|
||||
/// <summary>Добыча одной ходки - в руки духа (_carry), стопками не больше игровых.</summary>
|
||||
void DigTrip(int _ore)
|
||||
{
|
||||
if (_ore < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
(string item, int perDay, int perBlock) o = Ores[_ore];
|
||||
float mean = o.perDay / 24f * ProspectorYield;
|
||||
int n = Mathf.Max(1, Mathf.RoundToInt(mean * Random.Range(1f - ProspectorSpread, 1f + ProspectorSpread)));
|
||||
Carry(o.item, n);
|
||||
string log = o.item + " x" + n;
|
||||
if (o.perBlock > 0)
|
||||
{
|
||||
int blocks = Mathf.Max(1, Mathf.RoundToInt(n / (float)o.perBlock));
|
||||
foreach ((string item, float prob) f in Finds)
|
||||
{
|
||||
int k = 0;
|
||||
for (int b = 0; b < blocks; b++)
|
||||
{
|
||||
if (Random.value < f.prob)
|
||||
{
|
||||
k++;
|
||||
}
|
||||
}
|
||||
if (k > 0)
|
||||
{
|
||||
Carry(f.item, k);
|
||||
log += ", " + f.item + " x" + k;
|
||||
}
|
||||
}
|
||||
log += " (блоков руды " + blocks + ")";
|
||||
}
|
||||
Debug.Log("[NecromancerTome] Cinerarium: старатель " + ToWorldPos() + " - ходка: " + log);
|
||||
}
|
||||
|
||||
void Carry(string _item, int _n)
|
||||
{
|
||||
ItemValue iv = ItemClass.GetItem(_item);
|
||||
if (iv == null || iv.type <= 0)
|
||||
{
|
||||
Debug.LogWarning("[NecromancerTome] Cinerarium: старатель - нет предмета " + _item);
|
||||
return;
|
||||
}
|
||||
int max = Mathf.Max(1, iv.ItemClass.Stacknumber.Value);
|
||||
while (_n > 0)
|
||||
{
|
||||
int k = Mathf.Min(_n, max);
|
||||
_carry.Add(new ItemStack(iv.Clone(), k));
|
||||
_n -= k;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Добычу - в ящики по нитям; что не влезло, остаётся у духа.</summary>
|
||||
void Deliver(World _world)
|
||||
{
|
||||
List<TEFeatureStorage> boxes = new List<TEFeatureStorage>();
|
||||
void Add(Vector3i _p)
|
||||
{
|
||||
TEFeatureStorage s = CinerariumWork.BoxAt(_world, _p);
|
||||
if (s != null && !boxes.Contains(s))
|
||||
{
|
||||
boxes.Add(s);
|
||||
}
|
||||
}
|
||||
if (HasThreadTo)
|
||||
{
|
||||
Add(ThreadTo);
|
||||
}
|
||||
foreach (Vector3i p in ThreadOut)
|
||||
{
|
||||
Add(p);
|
||||
}
|
||||
foreach (Vector3i p in ThreadFrom)
|
||||
{
|
||||
Add(p);
|
||||
}
|
||||
if (boxes.Count == 0)
|
||||
{
|
||||
return; // ящик открыт игроком или нитей нет - подождать
|
||||
}
|
||||
for (int i = _carry.Count - 1; i >= 0; i--)
|
||||
{
|
||||
ItemStack s = _carry[i];
|
||||
CinerariumWork.Place(s, boxes);
|
||||
if (s.count <= 0)
|
||||
{
|
||||
_carry.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Куда дух спускается (20.10: «отлетает от урны на ближайший свободный блок, опускается на 5 блоков вниз»): из
|
||||
/// восьми соседних клеток - первая свободная (воздух или растение), сначала по сторонам; нет - над урной. Низ клетки, мир.</summary>
|
||||
Vector3 DigStand(World _world)
|
||||
{
|
||||
Vector3i urn = ToWorldPos();
|
||||
Vector3i[] dirs =
|
||||
{
|
||||
Vector3i.forward, Vector3i.right, Vector3i.back, Vector3i.left,
|
||||
new Vector3i(1, 0, 1), new Vector3i(1, 0, -1), new Vector3i(-1, 0, -1), new Vector3i(-1, 0, 1)
|
||||
};
|
||||
foreach (Vector3i d in dirs)
|
||||
{
|
||||
BlockValue bv = _world.GetBlock(urn + d);
|
||||
if (bv.isair || bv.Block is BlockPlant)
|
||||
{
|
||||
return World.blockToTransformPos(urn + d);
|
||||
}
|
||||
}
|
||||
return World.blockToTransformPos(urn + Vector3i.up);
|
||||
}
|
||||
|
||||
/// <summary>Урну убрали или выгрузили - добыча в руках духа пропадает.</summary>
|
||||
void ProspectorReset()
|
||||
{
|
||||
if (_carry.Count > 0)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] Cinerarium: старатель " + ToWorldPos() + " - недонесённая добыча пропала (" + CarryCount + ")");
|
||||
}
|
||||
_carry.Clear();
|
||||
_under = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Спуск духа старателя под землю и подъём (20.10: «опускается на 5 блоков вниз, постепенно становясь полностью
|
||||
/// прозрачным; обратно - так же»). Яркость - MaterialPropertyBlock на рендерерах духа (материалы общие не трогаются): дух
|
||||
/// светится сложением, поэтому «прозрачнее» = тусклее цвет и свечение; глаза гаснут на половине.</summary>
|
||||
public partial class CinerariumGenieMotion
|
||||
{
|
||||
/// <summary>0 - нет; 1 - летит к месту и спускается; 2 - под землёй (не виден); 3 - поднимается и летит домой.</summary>
|
||||
int _digMode;
|
||||
float _digT;
|
||||
Vector3 _digFrom, _digHover;
|
||||
float _digDepth;
|
||||
public static float SinkSeconds = 2.5f;
|
||||
List<Renderer> _fadeRenderers;
|
||||
static readonly MaterialPropertyBlock _fadeBlock = new MaterialPropertyBlock();
|
||||
static readonly int ColorId = Shader.PropertyToID("_Color");
|
||||
static readonly int EmissionId = Shader.PropertyToID("_EmissionColor");
|
||||
float _fade = 1f;
|
||||
|
||||
/// <summary>Под землёй или на пути туда/обратно.</summary>
|
||||
public bool Digging => _digMode != 0;
|
||||
|
||||
bool DigHidden => _digMode == 2;
|
||||
|
||||
/// <summary>Лететь к клетке _stand (низ клетки, координаты сцены) и уйти на _depth блоков вниз.</summary>
|
||||
public void Dive(Vector3 _stand, int _depth)
|
||||
{
|
||||
if (transform.parent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_t = -1f;
|
||||
Gesture(0f);
|
||||
_digFrom = transform.localPosition;
|
||||
_digHover = DigHover(_stand);
|
||||
_digDepth = _depth;
|
||||
_digMode = 1;
|
||||
_digT = 0f;
|
||||
}
|
||||
|
||||
/// <summary>Сразу под землёй (дух появился заново, а работа идёт).</summary>
|
||||
public void Bury(Vector3 _stand, int _depth)
|
||||
{
|
||||
if (transform.parent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_digHover = DigHover(_stand);
|
||||
_digDepth = _depth;
|
||||
_digMode = 2;
|
||||
transform.localPosition = _digHover + Vector3.down * _digDepth;
|
||||
}
|
||||
|
||||
/// <summary>Подняться и вернуться к урне; через сколько секунд будет дома.</summary>
|
||||
public float Rise()
|
||||
{
|
||||
if (_digMode == 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
_digMode = 3;
|
||||
_digT = 0f;
|
||||
return SinkSeconds + Mathf.Max(BackSeconds, Vector3.Distance(_digHover, Home()) / FlySpeed);
|
||||
}
|
||||
|
||||
Vector3 DigHover(Vector3 _stand)
|
||||
{
|
||||
return transform.parent.InverseTransformPoint(_stand) + new Vector3(0f, CinerariumGenie.OffsetY + Drop + 0.15f, 0f);
|
||||
}
|
||||
|
||||
/// <summary>Шаг спуска/подъёма; false - дух не копает, обычное поведение.</summary>
|
||||
bool DigUpdate(bool _asleep)
|
||||
{
|
||||
if (_digMode == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_asleep)
|
||||
{
|
||||
// Уснул - без полёта: спуск сразу закончен, подъём - сразу дома.
|
||||
if (_digMode == 1)
|
||||
{
|
||||
_digMode = 2;
|
||||
}
|
||||
else if (_digMode == 3)
|
||||
{
|
||||
_digMode = 0;
|
||||
SetFade(1f);
|
||||
transform.localPosition = Home();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
_digT += Time.deltaTime;
|
||||
Vector3 bottom = _digHover + Vector3.down * _digDepth;
|
||||
float fly = Mathf.Max(OutSeconds, Vector3.Distance(_digFrom, _digHover) / FlySpeed);
|
||||
if (_digMode == 1)
|
||||
{
|
||||
if (_digT < fly)
|
||||
{
|
||||
transform.localPosition = Vector3.Lerp(_digFrom, _digHover, Mathf.SmoothStep(0f, 1f, _digT / fly));
|
||||
Face(_digHover - _digFrom);
|
||||
SetFade(1f);
|
||||
}
|
||||
else if (_digT < fly + SinkSeconds)
|
||||
{
|
||||
float s = (_digT - fly) / SinkSeconds;
|
||||
transform.localPosition = Vector3.Lerp(_digHover, bottom, Mathf.SmoothStep(0f, 1f, s));
|
||||
SetFade(1f - s);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digMode = 2;
|
||||
transform.localPosition = bottom;
|
||||
}
|
||||
}
|
||||
else if (_digMode == 2)
|
||||
{
|
||||
transform.localPosition = bottom;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 home = Home();
|
||||
float back = Mathf.Max(BackSeconds, Vector3.Distance(_digHover, home) / FlySpeed);
|
||||
if (_digT < SinkSeconds)
|
||||
{
|
||||
float s = _digT / SinkSeconds;
|
||||
transform.localPosition = Vector3.Lerp(bottom, _digHover, Mathf.SmoothStep(0f, 1f, s));
|
||||
SetFade(s);
|
||||
}
|
||||
else if (_digT < SinkSeconds + back)
|
||||
{
|
||||
SetFade(1f);
|
||||
transform.localPosition = Vector3.Lerp(_digHover, home, Mathf.SmoothStep(0f, 1f, (_digT - SinkSeconds) / back));
|
||||
Face(home - _digHover);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digMode = 0;
|
||||
SetFade(1f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Яркость духа долей _k (1 - как обычно, свойства сняты).</summary>
|
||||
void SetFade(float _k)
|
||||
{
|
||||
_k = Mathf.Clamp01(_k);
|
||||
if (Mathf.Abs(_k - _fade) < 0.01f && (_k < 1f || _fade >= 1f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_fade = _k;
|
||||
if (_fadeRenderers == null)
|
||||
{
|
||||
_fadeRenderers = new List<Renderer>(GetComponentsInChildren<Renderer>(true));
|
||||
}
|
||||
foreach (Renderer r in _fadeRenderers)
|
||||
{
|
||||
if (r == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (r.name.Contains("eye"))
|
||||
{
|
||||
r.forceRenderingOff = _k < 0.5f;
|
||||
continue;
|
||||
}
|
||||
if (_k >= 1f)
|
||||
{
|
||||
r.SetPropertyBlock(null);
|
||||
continue;
|
||||
}
|
||||
Material m = r.sharedMaterial;
|
||||
if (m == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
r.GetPropertyBlock(_fadeBlock);
|
||||
if (m.HasProperty(ColorId))
|
||||
{
|
||||
_fadeBlock.SetColor(ColorId, m.GetColor(ColorId) * _k);
|
||||
}
|
||||
if (m.HasProperty(EmissionId))
|
||||
{
|
||||
_fadeBlock.SetColor(EmissionId, m.GetColor(EmissionId) * _k);
|
||||
}
|
||||
r.SetPropertyBlock(_fadeBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Консоль старателя: выключатель, внеочередная ходка, множитель добычи, состояние урн.</summary>
|
||||
[Preserve]
|
||||
public class ConsoleCmdNecroProspector : ConsoleCmdAbstract
|
||||
{
|
||||
public override bool IsExecuteOnClient => true;
|
||||
|
||||
public override bool AllowedInMainMenu => false;
|
||||
|
||||
public override string[] getCommands()
|
||||
{
|
||||
return new string[] { "necroprospector" };
|
||||
}
|
||||
|
||||
public override string getDescription()
|
||||
{
|
||||
return "Necromancer's Tome: Prospector Cinerarium - switch, extra trip, yield multiplier, status.";
|
||||
}
|
||||
|
||||
public override string getHelp()
|
||||
{
|
||||
return "necroprospector - status of every loaded prospector urn\n" +
|
||||
"necroprospector on|off - off: spirits stay home, no trips\n" +
|
||||
"necroprospector trip - every digging spirit comes back with a trip right now\n" +
|
||||
"necroprospector yield <x> - trip size multiplier (1 = as designed)\n" +
|
||||
"necroprospector spread <x> - random spread of a trip (0.2 = ±20%)\n" +
|
||||
"necroprospector depth <n> - how many blocks the spirit sinks";
|
||||
}
|
||||
|
||||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
|
||||
{
|
||||
SdtdConsole con = SingletonMonoBehaviour<SdtdConsole>.Instance;
|
||||
World w = GameManager.Instance?.World;
|
||||
if (_params.Count >= 1)
|
||||
{
|
||||
string cmd = _params[0].ToLowerInvariant();
|
||||
float v = 0f;
|
||||
bool num = _params.Count >= 2 && float.TryParse(_params[1], NumberStyles.Float, CultureInfo.InvariantCulture, out v);
|
||||
switch (cmd)
|
||||
{
|
||||
case "on":
|
||||
case "off":
|
||||
TEFeatureCinerarium.ProspectorOn = cmd == "on";
|
||||
break;
|
||||
case "trip":
|
||||
TEFeatureCinerarium.ForceTrip++;
|
||||
break;
|
||||
case "yield" when num:
|
||||
TEFeatureCinerarium.ProspectorYield = Mathf.Max(0f, v);
|
||||
break;
|
||||
case "spread" when num:
|
||||
TEFeatureCinerarium.ProspectorSpread = Mathf.Clamp01(v);
|
||||
break;
|
||||
case "depth" when num:
|
||||
TEFeatureCinerarium.ProspectorDepth = Mathf.Clamp((int)v, 1, 30);
|
||||
break;
|
||||
default:
|
||||
con.Output("necroprospector: unknown '" + _params[0] + "' - see 'help necroprospector'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
con.Output("necroprospector: " + (TEFeatureCinerarium.ProspectorOn ? "on" : "OFF") + ", yield x" + TEFeatureCinerarium.ProspectorYield.ToString("F2", CultureInfo.InvariantCulture) +
|
||||
", spread " + TEFeatureCinerarium.ProspectorSpread.ToString("F2", CultureInfo.InvariantCulture) + ", depth " + TEFeatureCinerarium.ProspectorDepth);
|
||||
if (w == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (Vector3i p in CinerariumRegistry.All())
|
||||
{
|
||||
TEFeatureCinerarium u = CinerariumThreads.UrnAt(w, p);
|
||||
if (u == null || !u.IsProspector)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int left = u.TripMinutesLeft(w);
|
||||
con.Output(" " + p + ": " + u.ProspectorNow(w) + ", sample " + (u.Sample == null || u.Sample.IsEmpty() ? "-" : u.Sample.itemValue.ItemClass?.GetItemName()) +
|
||||
(left >= 0 ? ", trip in " + left + " game min" : "") + (u.CarryCount > 0 ? ", carrying " + u.CarryCount : ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user