Files
necromants-tome-7d2d-3-2/HarmonySrc/CinerariumCraft.cs
T
AlexCubeandClaude Opus 5.5 c208e3b9f9 Урна рабочего: подкладка сырья в кузню до изготовления (#19)
После изготовления очередь до подкладки не доходила: переплавленного
каждый шаг хватало на один цемент. Теперь подкладка первой, но только
в пустую или наполовину опустевшую ячейку входа.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:07:52 +03:00

699 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
namespace NecromancerTome
{
/// <summary>
/// Работа духа урны рабочего (18.10.2026, BACKLOG «Урна рабочего»): изготовить предмет из ячейки образца, пока его в запасе
/// меньше, чем в стопке образца (23.10 - без счёта: пока хватает ресурсов и есть куда класть). Решения 17.10: нити ящик -> урна - откуда ингредиенты и топливо, урна -> станция - где
/// изготавливать, урна -> ящик - куда готовое (нет такой нити - готовое остаётся в выходе станции, выход полон - станция
/// гаснет); только прямые рецепты (цепочки - несколько урн); качество 1; топливо - костру, химстанции, кузне, и станция
/// гасится, когда делать нечего (верстаку, ступке, бетономешалке топливо не нужно). Изготавливает сама станция игры: дух
/// только кладёт рецепт в её очередь, забрав ингредиенты, - время, огонь и выход считает игра.
/// Запас = такие же предметы в ящиках «куда», в выходах станций и в их очередях.
/// Одно дело за шаг, как у прочих духов (CinerariumWork.Step), _dry - пробный шаг без изменений.
/// </summary>
public static class CinerariumCraft
{
const int FuelModule = 3, MaterialModule = 4;
sealed class Station
{
public Vector3i Pos;
public TileEntityWorkstation Te;
public string[] Areas;
}
public static Vector3i? Step(World _world, TEFeatureCinerarium _urn, bool _dry, out bool _stuck, out Vector3i? _from)
{
_stuck = false;
_from = null;
ItemStack sample = _urn.Sample;
List<(Vector3i p, TEFeatureStorage box)> src = new List<(Vector3i, TEFeatureStorage)>();
foreach (Vector3i p in _urn.ThreadFrom)
{
TEFeatureStorage b = CinerariumWork.BoxAt(_world, p);
if (b != null)
{
src.Add((p, b));
}
}
List<Station> stations = new List<Station>();
List<(Vector3i p, TEFeatureStorage box)> outs = new List<(Vector3i, TEFeatureStorage)>();
foreach (Vector3i p in _urn.ThreadOut)
{
TileEntityWorkstation te = CinerariumThreads.StationAt(_world, p);
if (te != null)
{
if (!te.IsUserAccessing())
{
stations.Add(new Station { Pos = p, Te = te, Areas = AreasOf(_world.GetBlock(p).Block) });
}
continue;
}
TEFeatureStorage b = CinerariumWork.BoxAt(_world, p);
if (b != null)
{
outs.Add((p, b));
}
}
bool hasSample = sample != null && !sample.IsEmpty();
// 1. Готовое (только предмет образца - чужое изготовленное игроком не трогаем) - из выхода станции в ящики «куда».
if (hasSample && outs.Count > 0)
{
foreach (Station st in stations)
{
ItemStack[] output = st.Te.Output;
for (int i = 0; i < output.Length; i++)
{
ItemStack s = output[i];
if (s == null || s.IsEmpty() || s.itemValue.type != sample.itemValue.type)
{
continue;
}
Vector3i? dst = null;
foreach ((Vector3i p, TEFeatureStorage box) o in outs)
{
if (CinerariumWork.Fits(o.box, s))
{
dst = o.p;
break;
}
}
if (!dst.HasValue)
{
_stuck = true; // ящики «куда» полны
continue;
}
_from = st.Pos;
if (_dry)
{
return dst;
}
ItemStack moving = s.Clone();
List<TEFeatureStorage> boxes = new List<TEFeatureStorage>();
foreach ((Vector3i p, TEFeatureStorage box) o in outs)
{
boxes.Add(o.box);
}
CinerariumWork.Place(moving, boxes);
output[i] = moving.count > 0 ? moving : ItemStack.Empty.Clone();
st.Te.Output = output;
Debug.Log("[NecromancerTome] Cinerarium: рабочий перенёс " + (s.count - moving.count) + " " + s.itemValue.ItemClass?.GetItemName() + " из станции " + st.Pos);
return dst;
}
}
}
// 2. Изготовить. 23.10, пользователь: «Рабочий должен делать бесконечное количество по образцу. Я положил 1 цемент,
// а его задача наделать цемента столько, сколько возможно с учётом текущего количества ресурсов» - образец задаёт что,
// не сколько (было: пока запас меньше стопки образца). Стоп - нет ингредиентов или готовое класть некуда.
EntityPlayer owner = OwnerPlayer(_world, _urn);
bool outRoom = false;
foreach ((Vector3i p, TEFeatureStorage box) o in outs)
{
if (CinerariumWork.Fits(o.box, sample))
{
outRoom = true;
break;
}
}
// 2а. Кузня: вход сырья держать полным (24.10, пользователь: «Места не должны пустовать. Нужно чтобы заполнены были
// обе ячейки»). Раньше сырьё подкладывалось, только когда переплавленного не хватало даже на одну штуку, - кузня
// плавила остатки, и дух ставил цемент по одному. Подкладка - ДО изготовления (после него очередь до неё не
// доходила: переплавленного каждый шаг хватало на одну штуку), но только в пустую или наполовину опустевшую ячейку -
// кузня плавит непрерывно, и иначе подкладка отнимала бы все шаги.
if (hasSample)
{
string feedName = sample.itemValue.ItemClass?.GetItemName();
foreach (Station st in stations)
{
if (!st.Te.isModuleUsed[MaterialModule] || !OutputHasRoom(st.Te, sample) && !outRoom)
{
continue;
}
foreach (Recipe r in CraftingManager.GetRecipes(feedName))
{
if (r.IsScrap || !AreaFits(r, st.Areas) || owner != null && !r.IsUnlocked(owner))
{
continue;
}
foreach (ItemStack ing in r.ingredients)
{
string iname = ing?.itemValue?.ItemClass?.GetItemName();
if (iname == null || UnitSlot(st.Te, iname) < 0)
{
continue;
}
if (FeedMaterial(st, iname, src, _dry, out Vector3i? rawAt, true))
{
_from = rawAt;
if (!_dry)
{
Ignite(st.Te, src, false);
}
return st.Pos;
}
}
}
}
}
if (hasSample)
{
string name = sample.itemValue.ItemClass?.GetItemName();
foreach (Station st in stations)
{
int slot = FreeQueueSlot(st.Te);
if (slot < 0)
{
continue;
}
if (!OutputHasRoom(st.Te, sample) && !outRoom)
{
continue; // выход полон и ящики «куда» полны (или их нет) - ниже станция погаснет
}
if (InQueue(st.Te, sample.itemValue.type))
{
continue; // партия этого предмета уже в очереди - следующая, когда она кончится
}
foreach (Recipe r in CraftingManager.GetRecipes(name))
{
if (r.IsScrap || !AreaFits(r, st.Areas) || !ToolFits(r, st.Te) || owner != null && !r.IsUnlocked(owner))
{
continue;
}
if (!Ingredients(r, st.Te, src, out Vector3i? srcAt, out string shortUnit, out int batches))
{
// Кузня: не хватает переплавленного материала - положить сырьё во вход (плавит сама, с огнём).
if (shortUnit != null && FeedMaterial(st, shortUnit, src, _dry, out Vector3i? rawAt))
{
_from = rawAt;
if (!_dry)
{
Ignite(st.Te, src, false);
}
return st.Pos;
}
continue;
}
_from = srcAt;
if (_dry)
{
return st.Pos;
}
TakeIngredients(r, st.Te, src, batches);
Enqueue(st.Te, slot, r, batches);
Ignite(st.Te, src, false);
Debug.Log("[NecromancerTome] Cinerarium: рабочий поставил " + name + " x" + batches + " на станцию " + st.Pos);
return st.Pos;
}
}
}
// 3. Огонь: работа есть - топливо и огонь; нет - погасить (экономия топлива).
foreach (Station st in stations)
{
if (!st.Te.isModuleUsed[FuelModule])
{
continue;
}
bool busy = st.Te.hasRecipeInQueue() || RawInInput(st.Te);
bool full = hasSample && !OutputHasRoom(st.Te, sample) && !outRoom;
if (busy && !full)
{
if (st.Te.BurnTotalTimeLeft <= 0f)
{
Vector3i? fuelAt = FuelSource(src);
if (fuelAt.HasValue)
{
_from = fuelAt;
if (_dry)
{
return st.Pos;
}
Ignite(st.Te, src, true);
return st.Pos;
}
}
else if (!_dry && !st.Te.IsBurning)
{
st.Te.IsBurning = true;
}
}
else if (st.Te.IsBurning)
{
// Погасить - тоже дело: дух летит к станции. Пробный шаг должен его найти - иначе настоящий шаг не
// запускается и огонь горел до конца топлива (проверка 18.10: костёр, химстанция, кузня).
if (_dry)
{
return st.Pos;
}
st.Te.IsBurning = false;
Debug.Log("[NecromancerTome] Cinerarium: рабочий погасил станцию " + st.Pos + (full ? " - выход полон" : " - делать нечего"));
if (full)
{
_stuck = true;
}
return st.Pos;
}
if (full)
{
_stuck = true;
}
}
return null;
}
/// <summary>Чьи рецепты изучены: хозяин урны, если он в игре; в одиночной - игрок. Нет никого - не проверяем.</summary>
static EntityPlayer OwnerPlayer(World _world, TEFeatureCinerarium _urn)
{
PlatformUserIdentifierAbs id = _urn.Parent?.Owner;
if (id != null)
{
PersistentPlayerData ppd = GameManager.Instance.persistentPlayers?.GetPlayerData(id);
if (ppd != null && _world.GetEntity(ppd.EntityId) is EntityPlayer p)
{
return p;
}
}
return _world.GetPrimaryPlayer();
}
/// <summary>Какие рецепты станция делает: Workstation.CraftingAreaRecipes или имя блока («player» - рецепты без станции).</summary>
static string[] AreasOf(Block _b)
{
if (_b == null)
{
return new string[0];
}
string a = _b.Properties.Contains("Workstation", "CraftingAreaRecipes") ? _b.Properties.GetString("Workstation", "CraftingAreaRecipes") : _b.GetBlockName();
return a.Replace(" ", "").Split(',');
}
static bool AreaFits(Recipe _r, string[] _areas)
{
string area = string.IsNullOrEmpty(_r.craftingArea) ? "player" : _r.craftingArea;
foreach (string a in _areas)
{
if (a.EqualsCaseInsensitive(area))
{
return true;
}
}
return false;
}
static bool ToolFits(Recipe _r, TileEntityWorkstation _te)
{
if (_r.craftingToolType == 0)
{
return true;
}
foreach (ItemStack t in _te.Tools)
{
if (t != null && !t.IsEmpty() && t.itemValue.type == _r.craftingToolType)
{
return true;
}
}
return false;
}
/// <summary>Свободная ячейка очереди: самая близкая к работающей (последней).</summary>
static int FreeQueueSlot(TileEntityWorkstation _te)
{
RecipeQueueItem[] q = _te.Queue;
for (int i = q.Length - 1; i >= 0; i--)
{
if (q[i] == null || q[i].Recipe == null || q[i].Multiplier <= 0)
{
return i;
}
}
return -1;
}
/// <summary>Есть ли в очереди станции рецепт этого предмета. 24.10, пользователь: партия - до стака, и «если бы их было
/// двое, то тут нужно занимать очередь на ячейку»: один предмет держит в очереди одну ячейку, прочие - другим урнам на
/// той же станции (иначе одна урна забила бы очередь стаками на часы вперёд).</summary>
static bool InQueue(TileEntityWorkstation _te, int _itemType)
{
foreach (RecipeQueueItem q in _te.Queue)
{
if (q != null && q.Recipe != null && q.Multiplier > 0 && q.Recipe.itemValueType == _itemType)
{
return true;
}
}
return false;
}
static bool OutputHasRoom(TileEntityWorkstation _te, ItemStack _sample)
{
foreach (ItemStack s in _te.Output)
{
if (s == null || s.IsEmpty())
{
return true;
}
int n = 1;
if (s.itemValue.type == _sample.itemValue.type && s.CanStackPartly(ref n))
{
return true;
}
}
return false;
}
/// <summary>Индекс ячейки переплавленного материала кузни для unit_X (вход 3 + номер материала), или -1.</summary>
static int UnitSlot(TileEntityWorkstation _te, string _itemName)
{
if (!_te.isModuleUsed[MaterialModule] || !_itemName.StartsWith("unit_"))
{
return -1;
}
string mat = _itemName.Substring(5);
for (int j = 0; j < _te.materialNames.Length; j++)
{
if (_te.materialNames[j].EqualsCaseInsensitive(mat) && 3 + j < _te.Input.Length)
{
return 3 + j;
}
}
return -1;
}
/// <summary>Хватает ли всего: переплавленное - в кузне, прочее - в ящиках «откуда». _srcAt - ящик, откуда первое;
/// _shortUnit - какого переплавленного не хватило (кузне можно подложить сырья). _batches - сколько раз рецепт ставится
/// в очередь за одно дело (24.10, пользователь: «в бетономешалку кладётся по одному камушку... ожидается, что он будет
/// класть хотя бы по целому стаку, а если на стак не хватает, то сколько найдёт»): на сколько хватает каждого
/// ингредиента, но не больше стака ни одного ингредиента и не больше стака готового.</summary>
static bool Ingredients(Recipe _r, TileEntityWorkstation _te, List<(Vector3i p, TEFeatureStorage box)> _src, out Vector3i? _srcAt, out string _shortUnit, out int _batches)
{
_srcAt = null;
_shortUnit = null;
ItemClass outClass = ItemClass.GetForId(_r.itemValueType);
_batches = outClass != null && _r.count > 0 ? Mathf.Max(1, outClass.MaxCount / _r.count) : 1;
_batches = Mathf.Min(_batches, short.MaxValue);
foreach (ItemStack ing in _r.ingredients)
{
if (ing == null || ing.IsEmpty() || ing.count <= 0)
{
continue;
}
string iname = ing.itemValue.ItemClass?.GetItemName() ?? "";
int us = UnitSlot(_te, iname);
if (us >= 0)
{
if (_te.Input[us].count < ing.count)
{
_shortUnit = iname;
return false;
}
_batches = Mathf.Min(_batches, _te.Input[us].count / ing.count);
continue;
}
int have = 0;
foreach ((Vector3i p, TEFeatureStorage box) s in _src)
{
ItemStack[] grid = s.box.ItemGrid.items;
for (int k = 0; k < grid.Length; k++)
{
if (grid[k] != null && !grid[k].IsEmpty() && grid[k].itemValue.type == ing.itemValue.type && !CinerariumWork.Locked(s.box, k))
{
have += grid[k].count;
if (!_srcAt.HasValue)
{
_srcAt = s.p;
}
}
}
}
if (have < ing.count)
{
return false;
}
int stack = ing.itemValue.ItemClass != null ? ing.itemValue.ItemClass.MaxCount : ing.count;
_batches = Mathf.Min(_batches, Mathf.Max(1, stack / ing.count), have / ing.count);
}
_batches = Mathf.Max(1, _batches);
return true;
}
static void TakeIngredients(Recipe _r, TileEntityWorkstation _te, List<(Vector3i p, TEFeatureStorage box)> _src, int _batches)
{
ItemStack[] input = _te.Input;
bool inputChanged = false;
foreach (ItemStack ing in _r.ingredients)
{
if (ing == null || ing.IsEmpty() || ing.count <= 0)
{
continue;
}
int us = UnitSlot(_te, ing.itemValue.ItemClass?.GetItemName() ?? "");
if (us >= 0)
{
input[us].count -= ing.count * _batches;
inputChanged = true;
continue;
}
int need = ing.count * _batches;
foreach ((Vector3i p, TEFeatureStorage box) s in _src)
{
ItemStack[] grid = s.box.ItemGrid.items;
for (int k = 0; k < grid.Length && need > 0; k++)
{
if (grid[k] != null && !grid[k].IsEmpty() && grid[k].itemValue.type == ing.itemValue.type && !CinerariumWork.Locked(s.box, k))
{
int take = Mathf.Min(need, grid[k].count);
CinerariumWork.Take(s.box, k, take);
need -= take;
}
}
if (need <= 0)
{
break;
}
}
}
if (inputChanged)
{
_te.Input = input;
}
}
static void Enqueue(TileEntityWorkstation _te, int _slot, Recipe _r, int _batches)
{
RecipeQueueItem[] q = _te.Queue;
float t = _r.craftingTime > 0f ? _r.craftingTime : 1f;
ItemClass outClass = ItemClass.GetForId(_r.itemValueType);
q[_slot] = new RecipeQueueItem
{
Recipe = _r,
Multiplier = (short)_batches,
CraftingTimeLeft = t,
OneItemCraftTime = t,
IsCrafting = _slot == q.Length - 1,
Quality = (byte)(outClass != null && outClass.HasQuality ? 1 : 0),
StartingEntityId = -1
};
_te.Queue = q;
}
static bool RawInInput(TileEntityWorkstation _te)
{
if (!_te.isModuleUsed[MaterialModule])
{
return false;
}
for (int i = 0; i < 3 && i < _te.Input.Length; i++)
{
if (_te.Input[i] != null && !_te.Input[i].IsEmpty())
{
return true;
}
}
return false;
}
/// <summary>Сырьё для переплавки в unit_X: предмет из ящика «откуда», сделанный из этого материала, - во вход кузни.</summary>
/// <remarks>_halfEmpty - только в пустую ячейку или заполненную меньше чем наполовину (подкладка впрок, шаг 2а).</remarks>
static bool FeedMaterial(Station _st, string _unit, List<(Vector3i p, TEFeatureStorage box)> _src, bool _dry, out Vector3i? _at, bool _halfEmpty = false)
{
_at = null;
string mat = _unit.Substring(5);
ItemStack[] input = _st.Te.Input;
// 23.10 («вместо камней вплавляет булыжник»): сначала сырьё, у которого нет рецепта (камни, лом), сделанное руками
// (булыжник) - только если сырья нет. Раньше - первое подходящее по порядку ячеек.
// 24.10 («работник положил сланец в переплавку в печку»): горючее (нефтяной сланец - сырьё для бензина) не плавится
// вовсе. 24.10 («и снова засунул булыжник!!!»): плавится только сырьё - сделанное руками (булыжник) не плавится
// никогда. Прежняя проверка «нет рецепта» не отличала камни от булыжника: у камня тоже есть рецепты (разбор в лом,
// выплавка из переплавленного) - см. Handmade.
foreach ((Vector3i p, TEFeatureStorage box, int k) s in Ordered(_src, g =>
{
ItemClass c = g.itemValue.ItemClass;
if (c?.MadeOfMaterial?.ForgeCategory == null || !c.MadeOfMaterial.ForgeCategory.EqualsCaseInsensitive(mat) ||
c.GetItemName().StartsWith("unit_") || ItemClass.GetFuelValue(g.itemValue) > 0 || Handmade(c))
{
return -1;
}
return 0;
}))
{
{
int k = s.k;
ItemStack g = s.box.ItemGrid.items[k];
ItemClass ic = g.itemValue.ItemClass;
for (int i = 0; i < 3 && i < input.Length; i++)
{
bool empty = input[i] == null || input[i].IsEmpty();
int room = empty ? ic.MaxCount : input[i].itemValue.type == g.itemValue.type ? ic.MaxCount - input[i].count : 0;
if (_halfEmpty && !empty && input[i].count * 2 > ic.MaxCount)
{
room = 0;
}
if (room <= 0)
{
continue;
}
_at = s.p;
if (_dry)
{
return true;
}
int take = Mathf.Min(room, g.count);
if (empty)
{
input[i] = new ItemStack(g.itemValue.Clone(), take);
}
else
{
input[i].count += take;
}
CinerariumWork.Take(s.box, k, take);
_st.Te.Input = input;
Debug.Log("[NecromancerTome] Cinerarium: рабочий положил в кузню " + _st.Pos + " " + take + " " + ic.GetItemName());
return true;
}
}
}
return false;
}
/// <summary>Сделано руками: есть рецепт изготовления - не разбор (IsScrap, wildcard_forge_category) и не выплавка в кузне
/// из переплавленного (material_based). Камни и лом - сырьё (только такие рецепты), булыжник - сделан руками.</summary>
static bool Handmade(ItemClass _c)
{
foreach (Recipe r in CraftingManager.GetRecipes(_c.GetItemName()))
{
if (!r.IsScrap && !r.wildcardForgeCategory && !r.materialBasedRecipe)
{
return true;
}
}
return false;
}
static Vector3i? FuelSource(List<(Vector3i p, TEFeatureStorage box)> _src)
{
foreach ((Vector3i p, TEFeatureStorage box, int k) s in Ordered(_src, FuelRank))
{
return s.p;
}
return null;
}
/// <summary>Порядок топлива (23.10: «вместо камней жжёт в печке сланец»): древесина, потом уголь, потом прочее горючее
/// (сланец, бензин - ценное сырьё, только если больше нечего). -1 - не горит.</summary>
static int FuelRank(ItemStack _g)
{
if (ItemClass.GetFuelValue(_g.itemValue) <= 0)
{
return -1;
}
string n = _g.itemValue.ItemClass?.GetItemName() ?? "";
return n.StartsWith("resourceWood") ? 0 : n.StartsWith("resourceCoal") ? 1 : 2;
}
/// <summary>Незакреплённые непустые ячейки ящиков «откуда», у которых _rank >= 0, по возрастанию ранга (при равном -
/// в прежнем порядке ящиков и ячеек).</summary>
static List<(Vector3i p, TEFeatureStorage box, int k)> Ordered(List<(Vector3i p, TEFeatureStorage box)> _src, System.Func<ItemStack, int> _rank)
{
List<(Vector3i p, TEFeatureStorage box, int k, int r, int i)> all = new List<(Vector3i, TEFeatureStorage, int, int, int)>();
foreach ((Vector3i p, TEFeatureStorage box) s in _src)
{
ItemStack[] grid = s.box.ItemGrid.items;
for (int k = 0; k < grid.Length; k++)
{
if (grid[k] == null || grid[k].IsEmpty() || CinerariumWork.Locked(s.box, k))
{
continue;
}
int r = _rank(grid[k]);
if (r >= 0)
{
all.Add((s.p, s.box, k, r, all.Count));
}
}
}
all.Sort((a, b) => a.r != b.r ? a.r.CompareTo(b.r) : a.i.CompareTo(b.i));
List<(Vector3i p, TEFeatureStorage box, int k)> res = new List<(Vector3i, TEFeatureStorage, int)>(all.Count);
foreach (var a in all)
{
res.Add((a.p, a.box, a.k));
}
return res;
}
/// <summary>Огонь станции с топливом: нет топлива - стопку горючего из ящиков «откуда» (_addFuel), затем зажечь.</summary>
static void Ignite(TileEntityWorkstation _te, List<(Vector3i p, TEFeatureStorage box)> _src, bool _addFuel)
{
if (!_te.isModuleUsed[FuelModule])
{
return;
}
if (_addFuel || _te.BurnTotalTimeLeft <= 0f)
{
ItemStack[] fuel = _te.Fuel;
foreach ((Vector3i p, TEFeatureStorage box, int k) s in Ordered(_src, FuelRank))
{
{
int k = s.k;
ItemStack g = s.box.ItemGrid.items[k];
for (int i = 0; i < fuel.Length; i++)
{
bool empty = fuel[i] == null || fuel[i].IsEmpty();
int max = g.itemValue.ItemClass.MaxCount;
int room = empty ? max : fuel[i].itemValue.type == g.itemValue.type ? max - fuel[i].count : 0;
if (room <= 0)
{
continue;
}
int take = Mathf.Min(room, g.count);
if (empty)
{
fuel[i] = new ItemStack(g.itemValue.Clone(), take);
}
else
{
fuel[i].count += take;
}
string fuelName = g.itemValue.ItemClass?.GetItemName(); // до Take: опустевшая ячейка очищает стопку (было NRE, 18.10)
CinerariumWork.Take(s.box, k, take);
_te.Fuel = fuel;
Debug.Log("[NecromancerTome] Cinerarium: рабочий подбросил топливо " + take + " " + fuelName);
goto lit;
}
}
}
}
lit:
if (_te.BurnTotalTimeLeft > 0f && !_te.IsBurning)
{
_te.IsBurning = true;
}
}
}
}