Некротехнолог: грядки; скакун крафтится 120 с
Дух последним делом собирает созревшее (метка crops, добыча Harvest) с идеальными бонусами и сразу сажает семя на то же место; засаживает пустые грядки случайными семенами из ящиков и сумки (можно ли сажать - решает сам блок семени). Описание урны - про универсального работника, 13 языков. Костяному скакуну задано craft_time 120 (было - сумма ингредиентов). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0890ffe1e9
commit
94bf0a5f5c
@@ -12,7 +12,10 @@ namespace NecromancerTome
|
||||
/// 2. забрать готовое из коллекторов (Items) и станций (TileEntityWorkstation.Output - верстак, кузня, химстанция,
|
||||
/// костёр, бетономешалка) и сложить в ящики зоны: сначала к таким же предметам, потом в свободное место; ящики полны -
|
||||
/// в сумку урны; некуда - дух чернеет и замирает над урной, оживает сам, когда место появится;
|
||||
/// 3. грядки - последними (ещё не сделано).
|
||||
/// 3. грядки - последними: собрать созревшее (блок с меткой crops и добычей Harvest) с «идеальными бонусами»
|
||||
/// (06.10: все бонусные доли урожая и семя - всегда), семя сразу посадить на то же место; затем засадить пустые
|
||||
/// грядки случайными семенами из ящиков и сумки (семя - предмет-блок стадии 1, BlockPlantGrowing; можно ли сажать,
|
||||
/// решает сам блок семени - CanPlaceBlockAt проверяет плодородие под ним, то есть грядку).
|
||||
/// Коллектор или станция подходят любые - будущий крольчатник обслуживается тем же кодом. Ящик - составной блок игрока
|
||||
/// с хранилищем (TEFeatureStorage, PlayerPlaced), кроме урн. Открытое у игрока (IsUserAccessing) не трогается.
|
||||
/// Работает на сервере: зовётся из TEFeatureCinerarium.UpdateTick.
|
||||
@@ -29,8 +32,13 @@ namespace NecromancerTome
|
||||
public readonly List<TileEntityWorkstation> Stations = new List<TileEntityWorkstation>();
|
||||
public readonly Dictionary<TileEntity, Vector3i> Where = new Dictionary<TileEntity, Vector3i>();
|
||||
public TEFeatureStorage Bag;
|
||||
public readonly List<Vector3i> Ripe = new List<Vector3i>();
|
||||
public readonly List<Vector3i> Empty = new List<Vector3i>();
|
||||
}
|
||||
|
||||
static readonly FastTags<TagGroup.Global> CropTag = FastTags<TagGroup.Global>.Parse("crops");
|
||||
static readonly GameRandom _rnd = GameRandomManager.Instance.CreateGameRandom();
|
||||
|
||||
static readonly HashSet<TileEntity> _seen = new HashSet<TileEntity>();
|
||||
|
||||
/// <summary>Одно дело. Возвращает, где дух работал (для полёта модели), или null; _stuck - есть что забрать, но некуда.</summary>
|
||||
@@ -68,10 +76,170 @@ namespace NecromancerTome
|
||||
return z.Where[w];
|
||||
}
|
||||
}
|
||||
// 3. Грядки.
|
||||
foreach (Vector3i p in z.Ripe)
|
||||
{
|
||||
if (Harvest(_world, p, z, ref blocked))
|
||||
{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
if (z.Empty.Count > 0)
|
||||
{
|
||||
Vector3i? planted = PlantRandom(_world, z);
|
||||
if (planted.HasValue)
|
||||
{
|
||||
return planted;
|
||||
}
|
||||
}
|
||||
_stuck = blocked;
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool IsRipe(Block _b)
|
||||
{
|
||||
return _b != null && !(_b is BlockPlantGrowing) && _b.HasAnyFastTags(CropTag) &&
|
||||
_b.itemsToDrop.TryGetValue(EnumDropEvent.Harvest, out List<Block.SItemDropProb> l) && l != null && l.Count > 0;
|
||||
}
|
||||
|
||||
static bool IsSeed(ItemValue _iv, out BlockValue _bv)
|
||||
{
|
||||
_bv = BlockValue.Air;
|
||||
if (_iv == null || _iv.type == 0 || !_iv.ItemClass.IsBlock())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_bv = _iv.ToBlockValue();
|
||||
return _bv.Block is BlockPlantGrowing;
|
||||
}
|
||||
|
||||
/// <summary>Урожай: все доли добычи Harvest (бонусы - тоже, вероятность только у обычных долей), множители сервера на
|
||||
/// урожай и семена. Одно семя - сразу в землю на то же место, остальное - в ящики/сумку. Нет места ни для чего -
|
||||
/// грядку не трогаем, дух чернеет.</summary>
|
||||
static bool Harvest(World _world, Vector3i _p, Zone _z, ref bool _blocked)
|
||||
{
|
||||
Block crop = _world.GetBlock(_p).Block;
|
||||
List<ItemStack> got = new List<ItemStack>();
|
||||
BlockValue replant = BlockValue.Air;
|
||||
foreach (Block.SItemDropProb d in crop.itemsToDrop[EnumDropEvent.Harvest])
|
||||
{
|
||||
ItemValue iv = ItemClass.GetItem(d.name);
|
||||
if (iv == null || iv.type == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool seed = IsSeed(iv, out BlockValue seedBv);
|
||||
bool bonus = d.tag != null && (d.tag.Contains("Bonus") || d.tag.Contains("bonus"));
|
||||
if (!bonus && d.prob < 1f && _rnd.RandomFloat > d.prob)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int n = _rnd.RandomRange(d.minCount, d.maxCount + 1);
|
||||
n = Mathf.RoundToInt(n * (seed ? XUiM_Recipes.SeedDropOutputModifier : XUiM_Recipes.CropOutputModifier));
|
||||
if (seed && replant.isair && n > 0)
|
||||
{
|
||||
replant = seedBv;
|
||||
n--;
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
got.Add(new ItemStack(iv, n));
|
||||
}
|
||||
}
|
||||
if (got.Count > 0 && !HasRoom(got[0], _z))
|
||||
{
|
||||
_blocked = true;
|
||||
return false;
|
||||
}
|
||||
_world.SetBlockRPC(_p, BlockValue.Air);
|
||||
if (!replant.isair)
|
||||
{
|
||||
replant.rotation = (byte)_rnd.RandomRange(0, 4);
|
||||
_world.SetBlockRPC(_p, replant);
|
||||
}
|
||||
foreach (ItemStack s in got)
|
||||
{
|
||||
Place(s, _z.Boxes);
|
||||
if (s.count > 0 && _z.Bag != null)
|
||||
{
|
||||
Place(s, new[] { _z.Bag });
|
||||
}
|
||||
if (s.count > 0)
|
||||
{
|
||||
// Влезло не всё (проверяли только первую долю) - остаток падает у урны, не пропадает.
|
||||
GameManager.Instance.ItemDropServer(s.Clone(), World.blockToTransformPos(_p) + Vector3.up * 0.5f, Vector3.zero);
|
||||
}
|
||||
}
|
||||
Debug.Log("[NecromancerTome] Cinerarium: дух собрал " + crop.GetBlockName() + " в " + _p + (replant.isair ? "" : ", посадил снова"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HasRoom(ItemStack _s, Zone _z)
|
||||
{
|
||||
foreach (TEFeatureStorage t in _z.Boxes)
|
||||
{
|
||||
if (Fits(t, _s))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return _z.Bag != null && Fits(_z.Bag, _s);
|
||||
}
|
||||
|
||||
static bool Fits(TEFeatureStorage _t, ItemStack _s)
|
||||
{
|
||||
for (int i = 0; i < _t.ItemGrid.Length; i++)
|
||||
{
|
||||
ItemStack x = _t.ItemGrid[i];
|
||||
if (x == null || x.IsEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
int n = _s.count;
|
||||
if (x.itemValue.type == _s.itemValue.type && x.CanStackPartly(ref n))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Засадить одну пустую грядку случайным семенем из сумки и ящиков (06.10: «случайно сажает имеющиеся семена»).</summary>
|
||||
static Vector3i? PlantRandom(World _world, Zone _z)
|
||||
{
|
||||
List<(TEFeatureStorage src, int slot, BlockValue bv)> seeds = new List<(TEFeatureStorage, int, BlockValue)>();
|
||||
foreach (TEFeatureStorage src in Sources(_z))
|
||||
{
|
||||
for (int k = 0; k < src.ItemGrid.Length; k++)
|
||||
{
|
||||
ItemStack st = src.ItemGrid[k];
|
||||
if (st != null && !st.IsEmpty() && !Locked(src, k) && IsSeed(st.itemValue, out BlockValue bv))
|
||||
{
|
||||
seeds.Add((src, k, bv));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (seeds.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
foreach (Vector3i p in _z.Empty)
|
||||
{
|
||||
var pick = seeds[_rnd.RandomRange(0, seeds.Count)];
|
||||
BlockValue bv = pick.bv;
|
||||
bv.rotation = (byte)_rnd.RandomRange(0, 4);
|
||||
if (!bv.Block.CanPlaceBlockAt(_world, p, bv))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Take(pick.src, pick.slot, 1);
|
||||
_world.SetBlockRPC(p, bv);
|
||||
Debug.Log("[NecromancerTome] Cinerarium: дух посадил " + bv.Block.GetBlockName() + " в " + p);
|
||||
return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Zone Scan(World _world, TEFeatureCinerarium _urn)
|
||||
{
|
||||
Zone z = new Zone();
|
||||
@@ -93,6 +261,16 @@ namespace NecromancerTome
|
||||
BlockValue bv = _world.GetBlock(p);
|
||||
if (bv.isair)
|
||||
{
|
||||
// Пустая клетка над плодородным блоком - кандидат в грядку (окончательно решит блок семени).
|
||||
if (_world.GetBlock(p - Vector3i.up).Block?.blockMaterial?.FertileLevel > 0)
|
||||
{
|
||||
z.Empty.Add(p);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!bv.ischild && IsRipe(bv.Block))
|
||||
{
|
||||
z.Ripe.Add(p);
|
||||
continue;
|
||||
}
|
||||
Vector3i owner = bv.ischild ? p + bv.parent : p; // часть составного блока (верстак 2x1) - к главной клетке
|
||||
|
||||
Reference in New Issue
Block a user