diff --git a/Config/Localization.csv b/Config/Localization.csv index 5628016..bc34d56 100644 --- a/Config/Localization.csv +++ b/Config/Localization.csv @@ -100,7 +100,7 @@ blockcommand_effect_off,blocks,Block,,,,Disable Effect,,Effekt deaktivieren,Desa blockcommand_zone_show,blocks,Block,,,,Show Zone,,Zone anzeigen,Mostrar zona,Afficher la zone,Mostra zona,範囲を表示,구역 표시,Pokaż strefę,Mostrar zona,Показать зону,Bölgeyi Göster,显示范围,顯示範圍 blockcommand_zone_hide,blocks,Block,,,,Hide Zone,,Zone ausblenden,Ocultar zona,Masquer la zone,Nascondi zona,範囲を非表示,구역 숨기기,Ukryj strefę,Ocultar zona,Скрыть зону,Bölgeyi Gizle,隐藏范围,隱藏範圍 necroCinerarium,blocks,Block,,,,Harvest Cinerarium,,Ernte-Cinerarium,Cinerario de cosecha,Cinéraire des moissons,Cinerario del raccolto,収穫の納骨壺,수확의 유골함,Cinerarium żniw,Cinerário da colheita,Цинерарий,Hasat Küllüğü,丰收骨灰瓮,豐收骨灰甕 -necroCinerariumDesc,blocks,Block,,,,"A funeral urn that houses a skull taken from a red corpse. Put the skull in its trunk and a spirit worker tends the garden around the urn: a 5 by 5 area, from one block below the urn to four above. The urn's own storage is the spirit's bag. Hold E on the urn for the trunk, the bag or the zone. Picked up, the urn keeps its skull. Zones of two urns cannot overlap, and you may keep one urn for every 500 dead you have put to rest.",,"Eine Grabesurne für einen Schädel aus einer roten Leiche. Leg den Schädel in ihren Kofferraum, und ein Geisterarbeiter pflegt den Garten um die Urne: eine Fläche von 5 mal 5, von einem Block unter der Urne bis vier darüber. Der Speicher der Urne ist die Tasche des Geistes. Halte E an der Urne für Kofferraum, Tasche oder Zone. Aufgehoben behält die Urne ihren Schädel. Die Zonen zweier Urnen dürfen sich nicht überschneiden, und du darfst je 500 zur Ruhe gebrachte Tote eine Urne halten.","Una urna funeraria que alberga un cráneo sacado de un cadáver rojo. Pon el cráneo en su maletero y un espíritu trabajador cuidará el huerto alrededor de la urna: un área de 5 por 5, desde un bloque bajo la urna hasta cuatro por encima. El almacén de la urna es la bolsa del espíritu. Mantén E sobre la urna para el maletero, la bolsa o la zona. Al recogerla, la urna conserva su cráneo. Las zonas de dos urnas no pueden solaparse, y puedes tener una urna por cada 500 muertos a los que hayas dado descanso.","Une urne funéraire qui abrite un crâne pris sur un cadavre rouge. Placez le crâne dans son coffre et un esprit ouvrier entretient le jardin autour de l'urne : une zone de 5 sur 5, d'un bloc sous l'urne à quatre au-dessus. Le stockage de l'urne est le sac de l'esprit. Maintenez E sur l'urne pour le coffre, le sac ou la zone. Ramassée, l'urne garde son crâne. Les zones de deux urnes ne peuvent pas se chevaucher, et vous pouvez garder une urne pour 500 morts que vous avez mis au repos.","Un'urna funeraria che custodisce un teschio preso da un cadavere rosso. Metti il teschio nel suo bagagliaio e uno spirito lavoratore curerà l'orto intorno all'urna: un'area di 5 per 5, da un blocco sotto l'urna fino a quattro sopra. Il deposito dell'urna è la borsa dello spirito. Tieni premuto E sull'urna per il bagagliaio, la borsa o la zona. Raccolta, l'urna conserva il teschio. Le zone di due urne non possono sovrapporsi, e puoi tenere un'urna ogni 500 morti a cui hai dato riposo.",赤い死体から取った頭蓋骨を納める葬送の壺。頭蓋骨をトランクに入れると、霊の働き手が壺の周りの畑を世話する。範囲は5×5、壺の1ブロック下から4ブロック上まで。壺の収納が霊の鞄になる。壺に向けてEを長押しすると、トランク・鞄・範囲表示を選べる。拾い上げても頭蓋骨は壺に残る。2つの壺の範囲は重ねられず、眠らせた死者500体ごとに壺を1つ持てる。,"붉은 시체에서 얻은 두개골을 모시는 장례용 항아리. 두개골을 트렁크에 넣으면 영혼 일꾼이 항아리 주변의 밭을 돌본다. 범위는 5×5, 항아리 한 칸 아래부터 네 칸 위까지. 항아리의 저장 공간이 영혼의 가방이다. 항아리에 E를 길게 누르면 트렁크, 가방, 구역 표시를 고를 수 있다. 주워도 두개골은 항아리에 남는다. 두 항아리의 구역은 겹칠 수 없고, 안식시킨 망자 500명마다 항아리를 하나 둘 수 있다.","Urna pogrzebowa, w której spoczywa czaszka wzięta z czerwonych zwłok. Włóż czaszkę do jej bagażnika, a duch-robotnik zadba o ogród wokół urny: obszar 5 na 5, od bloku pod urną do czterech nad nią. Schowek urny to torba ducha. Przytrzymaj E na urnie: bagażnik, torba lub strefa. Podniesiona urna zachowuje czaszkę. Strefy dwóch urn nie mogą na siebie zachodzić, a jedną urnę możesz mieć na każde 500 zmarłych, których ukoiłeś.","Uma urna funerária que abriga um crânio tirado de um cadáver vermelho. Coloque o crânio no porta-malas e um espírito trabalhador cuida da horta ao redor da urna: uma área de 5 por 5, de um bloco abaixo da urna até quatro acima. O armazenamento da urna é a bolsa do espírito. Segure E na urna para o porta-malas, a bolsa ou a zona. Recolhida, a urna mantém o crânio. As zonas de duas urnas não podem se sobrepor, e você pode ter uma urna para cada 500 mortos que pôs para descansar.","Погребальная урна для черепа из красного трупа. Положите череп в её багажник - и дух-работник будет ухаживать за грядками вокруг урны: участок 5 на 5, от блока под урной до четырёх над ней. Хранилище урны - сумка духа. Удерживайте E на урне: багажник, сумка или зона. Поднятая урна сохраняет череп. Зоны двух урн не пересекаются; одну урну можно держать на каждые 500 упокоенных мертвецов.","Kırmızı bir cesetten alınmış kafatasını barındıran bir cenaze küpü. Kafatasını bagajına koy; bir ruh işçi küpün çevresindeki bahçeye bakar: 5'e 5'lik bir alan, küpün bir blok altından dört blok üstüne kadar. Küpün deposu ruhun çantasıdır. Bagaj, çanta ya da bölge için küpe E'yi basılı tut. Toplanan küp kafatasını korur. İki küpün bölgeleri çakışamaz; huzura kavuşturduğun her 500 ölü için bir küp tutabilirsin.",安放红色尸体头骨的葬瓮。把头骨放进它的后备箱,灵魂工人会照料瓮周围的田地:5×5的范围,从瓮下方1格到上方4格。瓮的储物空间就是灵魂的背包。对着瓮长按E可选择后备箱、背包或显示区域。捡起瓮时头骨仍留在其中。两个瓮的区域不能重叠;每安息500个亡者可拥有一个瓮。,安放紅色屍體頭骨的葬甕。把頭骨放進它的後車廂,靈魂工人會照料甕周圍的田地:5×5的範圍,從甕下方1格到上方4格。甕的儲物空間就是靈魂的背包。對著甕長按E可選擇後車廂、背包或顯示區域。撿起甕時頭骨仍留在其中。兩個甕的區域不能重疊;每安息500個亡者可擁有一個甕。 +necroCinerariumDesc,blocks,Block,,,,"A funeral urn that houses a skull taken from a red corpse. Put the skull in its trunk and a spirit rises from the urn to serve its 5 by 5 area, from one block below the urn to four above: it tops up dew collectors, apiaries and chicken coops from the boxes nearby, carries finished goods from workstations and collectors into those boxes, and last of all harvests and replants the garden. The urn's own storage is the spirit's bag for whatever the boxes cannot hold. Hold E on the urn for the bag, the trunk or the zone. Picked up, the urn keeps its skull. Zones of two urns cannot overlap, and you may keep one urn for every 500 dead you have put to rest.",,"Eine Grabesurne für einen Schädel aus einer roten Leiche. Leg den Schädel in ihren Kofferraum, und ein Geist steigt aus der Urne und versorgt ihre Fläche von 5 mal 5, von einem Block unter der Urne bis vier darüber: Er füllt Taukollektoren, Bienenstöcke und Hühnerställe aus den Kisten in der Nähe auf, trägt fertige Waren aus Werkbänken und Kollektoren in diese Kisten und erntet zuletzt den Garten ab und pflanzt neu. Der Speicher der Urne ist die Tasche des Geistes für alles, was die Kisten nicht fassen. Halte E an der Urne für Tasche, Kofferraum oder Zone. Aufgehoben behält die Urne ihren Schädel. Die Zonen zweier Urnen dürfen sich nicht überschneiden, und du darfst je 500 zur Ruhe gebrachte Tote eine Urne halten.","Una urna funeraria que alberga un cráneo sacado de un cadáver rojo. Pon el cráneo en su maletero y un espíritu surgirá de la urna para atender su área de 5 por 5, desde un bloque bajo la urna hasta cuatro por encima: repone recolectores de rocío, colmenas y gallineros con lo que hay en los cofres cercanos, lleva los productos terminados de las estaciones y recolectores a esos cofres y, por último, cosecha y vuelve a plantar el huerto. El almacén de la urna es la bolsa del espíritu para lo que no quepa en los cofres. Mantén E sobre la urna para la bolsa, el maletero o la zona. Al recogerla, la urna conserva su cráneo. Las zonas de dos urnas no pueden solaparse, y puedes tener una urna por cada 500 muertos a los que hayas dado descanso.","Une urne funéraire qui abrite un crâne pris sur un cadavre rouge. Placez le crâne dans son coffre et un esprit s'élève de l'urne pour servir sa zone de 5 sur 5, d'un bloc sous l'urne à quatre au-dessus : il réapprovisionne collecteurs de rosée, ruchers et poulaillers à partir des caisses voisines, porte les produits finis des établis et des collecteurs dans ces caisses et, en dernier, récolte et replante le jardin. Le stockage de l'urne est le sac de l'esprit pour ce que les caisses ne peuvent contenir. Maintenez E sur l'urne pour le sac, le coffre ou la zone. Ramassée, l'urne garde son crâne. Les zones de deux urnes ne peuvent pas se chevaucher, et vous pouvez garder une urne pour 500 morts que vous avez mis au repos.","Un'urna funeraria che custodisce un teschio preso da un cadavere rosso. Metti il teschio nel suo bagagliaio e uno spirito sorgerà dall'urna per servire la sua area di 5 per 5, da un blocco sotto l'urna fino a quattro sopra: rifornisce raccoglitori di rugiada, apiari e pollai con ciò che c'è nelle casse vicine, porta i prodotti finiti da stazioni e raccoglitori in quelle casse e, per ultimo, raccoglie e ripianta l'orto. Il deposito dell'urna è la borsa dello spirito per ciò che le casse non contengono. Tieni premuto E sull'urna per la borsa, il bagagliaio o la zona. Raccolta, l'urna conserva il teschio. Le zone di due urne non possono sovrapporsi, e puoi tenere un'urna ogni 500 morti a cui hai dato riposo.",赤い死体から取った頭蓋骨を納める葬送の壺。頭蓋骨をトランクに入れると、壺から霊が立ち上り、5×5の範囲(壺の1ブロック下から4ブロック上まで)を世話する。近くの箱から露集め器・養蜂箱・鶏小屋に消耗品を補充し、作業台や収集器の完成品をその箱へ運び、最後に畑の収穫と植え直しを行う。壺の収納は、箱に入りきらない物のための霊の鞄。壺に向けてEを長押しすると、鞄・トランク・範囲表示を選べる。拾い上げても頭蓋骨は壺に残る。2つの壺の範囲は重ねられず、眠らせた死者500体ごとに壺を1つ持てる。,"붉은 시체에서 얻은 두개골을 모시는 장례용 항아리. 두개골을 트렁크에 넣으면 항아리에서 영혼이 솟아올라 5×5 구역(항아리 한 칸 아래부터 네 칸 위까지)을 돌본다. 근처 상자에서 이슬 수집기, 양봉장, 닭장에 소모품을 채우고, 작업대와 수집기의 완성품을 그 상자로 옮기며, 마지막으로 밭을 수확하고 다시 심는다. 항아리의 저장 공간은 상자에 들어가지 않는 것을 담는 영혼의 가방이다. 항아리에 E를 길게 누르면 가방, 트렁크, 구역 표시를 고를 수 있다. 주워도 두개골은 항아리에 남는다. 두 항아리의 구역은 겹칠 수 없고, 안식시킨 망자 500명마다 항아리를 하나 둘 수 있다.","Urna pogrzebowa, w której spoczywa czaszka wzięta z czerwonych zwłok. Włóż czaszkę do jej bagażnika, a z urny uniesie się duch, który obsługuje obszar 5 na 5, od bloku pod urną do czterech nad nią: uzupełnia kolektory rosy, pasieki i kurniki tym, co leży w pobliskich skrzyniach, przenosi gotowe wyroby ze stanowisk i kolektorów do tych skrzyń, a na końcu zbiera plony i obsadza ogród na nowo. Schowek urny to torba ducha na to, co nie zmieści się w skrzyniach. Przytrzymaj E na urnie: torba, bagażnik lub strefa. Podniesiona urna zachowuje czaszkę. Strefy dwóch urn nie mogą na siebie zachodzić, a jedną urnę możesz mieć na każde 500 zmarłych, których ukoiłeś.","Uma urna funerária que abriga um crânio tirado de um cadáver vermelho. Coloque o crânio no porta-malas e um espírito surge da urna para cuidar da área de 5 por 5, de um bloco abaixo da urna até quatro acima: reabastece coletores de orvalho, apiários e galinheiros com o que há nos baús próximos, leva os produtos prontos das estações e coletores para esses baús e, por último, colhe e replanta a horta. O armazenamento da urna é a bolsa do espírito para o que não couber nos baús. Segure E na urna para a bolsa, o porta-malas ou a zona. Recolhida, a urna mantém o crânio. As zonas de duas urnas não podem se sobrepor, e você pode ter uma urna para cada 500 mortos que pôs para descansar.","Погребальная урна для черепа из красного трупа. Положите череп в её багажник - и из урны поднимется дух, который обслуживает участок 5 на 5, от блока под урной до четырёх над ней: подкладывает из ящиков рядом расходники сборщикам росы, пасекам и курятникам, переносит готовое из станций и коллекторов в эти ящики, а в последнюю очередь собирает урожай и засаживает грядки. Хранилище урны - сумка духа для того, что не влезло в ящики. Удерживайте E на урне: сумка, багажник или зона. Поднятая урна сохраняет череп. Зоны двух урн не пересекаются; одну урну можно держать на каждые 500 упокоенных мертвецов.","Kırmızı bir cesetten alınmış kafatasını barındıran bir cenaze küpü. Kafatasını bagajına koy; küpten bir ruh yükselir ve 5'e 5'lik alana, küpün bir blok altından dört blok üstüne kadar bakar: yakındaki sandıklardan çiy toplayıcılara, arılıklara ve kümeslere sarf malzemesi koyar, tezgâhlardaki ve toplayıcılardaki hazır ürünleri bu sandıklara taşır ve en son bahçeyi hasat edip yeniden eker. Küpün deposu, sandıklara sığmayanlar için ruhun çantasıdır. Çanta, bagaj ya da bölge için küpe E'yi basılı tut. Toplanan küp kafatasını korur. İki küpün bölgeleri çakışamaz; huzura kavuşturduğun her 500 ölü için bir küp tutabilirsin.",安放红色尸体头骨的葬瓮。把头骨放进它的后备箱,瓮中便会升起一个灵魂,照料5×5的范围(瓮下方1格到上方4格):用附近箱子里的物品为集露器、蜂房和鸡舍补充消耗品,把工作台和收集器的成品搬进这些箱子,最后收割并重新种植田地。瓮的储物空间是灵魂的背包,用来存放箱子装不下的东西。对着瓮长按E可选择背包、后备箱或显示区域。捡起瓮时头骨仍留在其中。两个瓮的区域不能重叠;每安息500个亡者可拥有一个瓮。,安放紅色屍體頭骨的葬甕。把頭骨放進它的後車廂,甕中便會升起一個靈魂,照料5×5的範圍(甕下方1格到上方4格):用附近箱子裡的物品為集露器、蜂房和雞舍補充消耗品,把工作台和收集器的成品搬進這些箱子,最後收割並重新種植田地。甕的儲物空間是靈魂的背包,用來存放箱子裝不下的東西。對著甕長按E可選擇背包、後車廂或顯示區域。撿起甕時頭骨仍留在其中。兩個甕的區域不能重疊;每安息500個亡者可擁有一個甕。 necroCinerariumSkull,blocks,Block,,,,Harvest Cinerarium (with skull),,Ernte-Cinerarium (mit Schädel),Cinerario de cosecha (con cráneo),Cinéraire des moissons (avec crâne),Cinerario del raccolto (con teschio),収穫の納骨壺(頭蓋骨入り),수확의 유골함 (두개골 포함),Cinerarium żniw (z czaszką),Cinerário da colheita (com crânio),Цинерарий с черепом,Hasat Küllüğü (kafatasıyla),丰收骨灰瓮(含头骨),豐收骨灰甕(含頭骨) blockcommand_TEFeatureCinerarium:trunk,blocks,Block,,,,Trunk,,Kofferraum,Maletero,Coffre,Bagagliaio,トランク,트렁크,Bagażnik,Porta-malas,Багажник,Bagaj,后备箱,後車廂 blockcommand_trunk,blocks,Block,,,,Trunk,,Kofferraum,Maletero,Coffre,Bagagliaio,トランク,트렁크,Bagażnik,Porta-malas,Багажник,Bagaj,后备箱,後車廂 diff --git a/Config/recipes.xml b/Config/recipes.xml index 0dbecf4..fe285b7 100644 --- a/Config/recipes.xml +++ b/Config/recipes.xml @@ -493,7 +493,7 @@ гео-оссуария: транспорт нужен, когда карта уже велика для пеших ходок. Состав - по указанию пользователя: кости, гнилое мясо, прах, кожа жертвы, два флакона крови. --> - + @@ -587,7 +587,7 @@ - + diff --git a/HarmonySrc/CinerariumWork.cs b/HarmonySrc/CinerariumWork.cs index cf34972..2fab8db 100644 --- a/HarmonySrc/CinerariumWork.cs +++ b/HarmonySrc/CinerariumWork.cs @@ -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 Stations = new List(); public readonly Dictionary Where = new Dictionary(); public TEFeatureStorage Bag; + public readonly List Ripe = new List(); + public readonly List Empty = new List(); } + static readonly FastTags CropTag = FastTags.Parse("crops"); + static readonly GameRandom _rnd = GameRandomManager.Instance.CreateGameRandom(); + static readonly HashSet _seen = new HashSet(); /// Одно дело. Возвращает, где дух работал (для полёта модели), или null; _stuck - есть что забрать, но некуда. @@ -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 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; + } + + /// Урожай: все доли добычи Harvest (бонусы - тоже, вероятность только у обычных долей), множители сервера на + /// урожай и семена. Одно семя - сразу в землю на то же место, остальное - в ящики/сумку. Нет места ни для чего - + /// грядку не трогаем, дух чернеет. + static bool Harvest(World _world, Vector3i _p, Zone _z, ref bool _blocked) + { + Block crop = _world.GetBlock(_p).Block; + List got = new List(); + 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; + } + + /// Засадить одну пустую грядку случайным семенем из сумки и ящиков (06.10: «случайно сажает имеющиеся семена»). + 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) - к главной клетке diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index fe9cead..6c23567 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ