Добавлен , опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
Lua
Тип:
Наработка
Возможно вам хотелось сделать окружающее пространство более живым и органичным? Данная наработка предоставляет возможность поэтапной смены тайлов, демонстрирующей "зарастание" покрова. Для примера будут использованы стандартные тайлы, хотя вы всегда можете подставить свои.
Быстрый старт
code
-- синхронизируем lua рандом
-- только для 1.26
math.randomseed(GetRandomInt(0, 2147483648-1))

local world_rect = GetWorldBounds()
local minX, maxX, minY, maxY = GetRectMinX(world_rect), GetRectMaxX(world_rect), GetRectMinY(world_rect), GetRectMaxY(world_rect)
local step = 128 -- шаг сетки

local Timer = TimerSystem()
local ts_array = SquareArray(minX, maxX, minY, maxY, step) -- квадратный массив для создания TerrainSystem
local ts_set = function (x,y,typ) SetTerrainType(x,y,typ,math.random(4)-1,1,0) end
local ts_get = GetTerrainType
ter_sys = TerrainSystem{
	Timer = Timer,
	array = ts_array,
	get_type = ts_get,
	set_type = ts_set,
}

-- функция конвертации строки-типа в число, для того чтобы показать пример
local function FourCC(str)
	local n, len = 0, #str
	for i = len, 1, -1 do n = n + (str:byte(i,i) << 8*(len-i)) end -- shift by 0,8,16,24
	return n
end

-- создаем 3 участка
SetTerrainType(-200,200, FourCC('Ldrg'), 1, 3, 0)

SetTerrainType(200,-200, FourCC('Lrok'), 1, 3, 0)

SetTerrainType(-200,-200, FourCC('Ybtl'), 1, 3, 0)

-- запуск по всей карте
-- ter_sys:init() 


-- запуск в квадратном участке
-- for x = -384, 384, 128 do
-- 	for y = -384, 384, 128 do
-- 		ter_sys:start_tox(x, y)
-- 	end
-- end

-- Timer.after(1, function()
-- 	ter_sys:stop_tox(0,128)
-- 	ter_sys:modification(128,0, 5)
-- end)
API
local ter_sys = TerrainSystem(...) -- см быстрый старт
ter_sys:start_tox(real x, real y) --> true or false | запустить зарастание
ter_sys:stop_tox(real x, real y) --> остановить зарастание
ter_sys:modification(rela x, real y, real ratio) --> true or false | ускорить или замедлить
-- изначально скорость 1, передача -0.5 ускорит на 50%, передача 3. замедлит на 300%
ter_sys:init() -- запускает start_tox() по всей карте
ter_sys:IsEarth(real x, real y) -- true, если это один из тайлов земли
ter_sys:IsFootballGrass(real x, real y) -- true, если это футбольная трава
ter_sys:IsGreenGrass(real x, real y) -- true, если это зеленая трава
ter_sys:IsYellowGrass(real x, real y)  -- true, если это желтая трава
БД
-- types - цепочки превращений земли до FOOTBALL_GRASS 
-- type - тип почвы
-- time - время перехода до следующей стадии
local EARTH = { 
	{types = {'Ldrt', 'Ldrg', 'Fdrg'}, time = 3},
	{types = {'Lrok', 'Frok'}, time = 4},
}
local FOOTBALL_GRASS = {type = 'Lgrs', time = 5}
local GREEN_GRASS = {type = 'Lgrd', time = 6}
local YELLOW_GRASS = {type = 'Fgrd'}
По кнопке скачать находится архив с исходниками и их адаптированными вариантами для 1.26 и reforget.
Отдельно спасибо МрачныйВорон за спонсирование данной наработки
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
26
2 года назад
0
будет давать десинки на рефорже
2
17
2 года назад
2
Hate, почему?
0
26
2 года назад
0
потому что создаешь объекты вне мейн потока
5
37
2 года назад
5
Круто, но какое-то рандомное оно, вот бы в разные стороны разрасталось островками такими и каждый новый уровень от центра островка этого
3
26
2 года назад
Отредактирован Lord_Teo
3
Хотел поделиться своим ГУИ. Ни в коем случае не подумайте, что я хочу обесценить кого-нить или еще что-нибудь в таком духе. Просто мне стало интересно сделать такое на гуи, так сказать, доказать для себя, что такое возможно. Зарастание проходит в заданной области и с конкретной указанной точки. Утечки не убирал, потому что просто хотел получить демонстрацию.
П.С. а как вставить видео на сайт без ютуба?
Загруженные файлы
2
27
2 года назад
Отредактирован MpW
2
вот для рефорджа карта и скрипт пример заростания почвы
скрипт
do
    local InitGlobalsOrigin = InitGlobals
    function InitGlobals()
        InitGlobalsOrigin()



----------------------------------------------- SquareArray
if not SquareArray then
local floor = math.floor
local ceil = math.ceil


local function round(x) return x > 0 and floor(x+0.5) or ceil(x-0.5) end

local function norm_x(self, x)
	if x < self.minX or x > self.maxX then return nil
	else return x end
end

local function norm_y(self, y)
	if y < self.minY or y > self.maxY then return nil
	else return y end
end

local function norm_ix(self, ix)
	if ix < self.minIX or ix > self.maxIX then return nil
	else return ix end
end

local function norm_iy(self, iy)
	if iy < self.minIY or iy > self.maxIY then return nil
	else return iy end
end

local index = {}

function index:norm_xy(x,y) return norm_x(self, x), norm_y(self, y) end

function index:is_xy(x, y) return norm_x(self, x) and norm_y(self, y) end

function index:real_to_index(x, y)
	x, y = self:norm_xy(x, y)
	if not (x and y) then return nil end
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	local ix = round((x+offsetX)/step)
	local iy = round((y+offsetY)/step)
	return ix, iy
end

function index:get_xy(x, y)
	local ix, iy = self:real_to_index(x, y)
	return ix and self.grid[iy][ix] or nil
end

function index:set_xy(x, y, val)
	local ix, iy = self:real_to_index(x, y)
	if ix then self.grid[iy][ix] = val end
end

function index:get_ixiy(ix, iy)
	return self.grid[iy][ix]
end

function index:set_ixiy(ix, iy, val)
	self.grid[iy][ix] = val
end

function index:id_xy(x, y)
	local ix, iy = self:real_to_index(x, y)
	if ix then
		return iy*self.maxIX + ix
	else return nil end
end

function index:by_all_square()
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	local minIX, minIY, maxIX, maxIY = self.minIX, self.minIY, self.maxIX, self.maxIY
	-- print(offsetX, offsetY, step)
	-- print(minIX, minIY, maxIX, maxIY)
	local t_minIX = minIX - 1
	return function()
		t_minIX = t_minIX + 1
		if t_minIX > maxIX then
			t_minIX = minIX
			minIY = minIY + 1
		end
		if minIY <= maxIY then
			return t_minIX*step-offsetX, minIY*step-offsetY, t_minIX, minIY
		end
	end
end

local mt = {__index = index}

function SquareArray(minX, maxX, minY, maxY, step)
	local grid = {}
	local object = { minX=minX, maxX=maxX, minY=minY, maxY=maxY, step=step,
		offsetY = -minY, offsetX = -minX, players = {},
		minIX = 0, minIY = 0, maxIX = round((maxX-minX)/step), maxIY = round((maxY-minY)/step),
		grid = grid,
	}
	for i=0, object.maxIY do grid[i] = {} end
	return setmetatable(object, mt)
end
end
----------------------------------------------- Time
if not TimeSystem then
function TimeSystem(ptime)
	local list = {}
	local ht = {}
	local tick = 0
	local ptime = ptime or 0.03125
	local Time = {}

	-- @param func function(time) [[your code]] end
	local function subscribe(func)
		table.insert(list, func)
		ht[func] = true
	end

	local function unsubscribe(func)
		if ht[func] then
			for i=1, #list do
				if list[i] == func then table.remove(list, i); ht[func] = nil end
			end
		end
	end

	local function update()
		tick = tick + 1
		local time = tick*ptime
		Time.time = time
		for i=1, #list do
			list[i](time)
		end
	end
	stop = true
	local function run()
		while stop do
			update()
		end
	end

	Time.ptime = ptime
	Time.time = 0
	Time.subscribe = subscribe
	Time.unsubscribe = unsubscribe
	Time.run = run

	-- connect API game
	TimerStart(CreateTimer(), ptime, true, update)
	return Time
end
end
----------------------------------------------- TimerHeap
if not TimerHeap then
local assert = assert
local floor = math.floor
--================================================================
-- https://github.com/Tieske/binaryheap.lua/blob/master/src/binaryheap.lua
-- basic heap sorting algorithm
--================================================================

--- Basic heap.
-- This is the base implementation of the heap. Under regular circumstances
-- this should not be used, instead use a _Plain heap_ or _Unique heap_.
-- @section baseheap

--- Creates a new binary heap.
-- This is the core of all heaps, the others
-- are built upon these sorting functions.
-- @param swap (function) `swap(heap, idx1, idx2)` swaps values at
-- `idx1` and `idx2` in the heaps `heap.values` and `heap.payloads` lists (see
-- return value below).
-- @param erase (function) `swap(heap, position)` raw removal
-- @param lt (function) in `lt(a, b)` returns `true` when `a < b` (for a min-heap)
-- @return table with two methods; `heap:bubbleUp(pos)` and `heap:sinkDown(pos)`
-- that implement the sorting algorithm and two fields; `heap.values` and
-- `heap.payloads` being lists, holding the values and payloads respectively.
local binaryHeap = function(swap, erase, lt)

	local heap = {
			values = {},  -- list containing values
			erase = erase,
			swap = swap,
			lt = lt,
		}

	function heap:bubbleUp(pos)
		local values = self.values
		while pos>1 do
			local parent = floor(pos/2)
			if not lt(values[pos], values[parent]) then
					break
			end
			swap(self, parent, pos)
			pos = parent
		end
	end

	function heap:sinkDown(pos)
		local values = self.values
		local last = #values
		while true do
			local min = pos
			local child = 2 * pos

			for c = child, child + 1 do
				if c <= last and lt(values[c], values[min]) then min = c end
			end

			if min == pos then break end

			swap(self, pos, min)
			pos = min
		end
	end

	return heap
end

--================================================================
-- plain heap management functions
--================================================================

--- Plain heap.
-- A plain heap carries a single piece of information per entry. This can be
-- any type (except `nil`), as long as the comparison function used to create
-- the heap can handle it.
-- @section plainheap
do end -- luacheck: ignore
-- the above is to trick ldoc (otherwise `update` below disappears)

local update_old
--- Updates the value of an element in the heap.
-- @function heap:update
-- @param pos the position which value to update
-- @param newValue the new value to use for this payload
update_old = function(self, pos, newValue)
	assert(newValue ~= nil, "cannot add 'nil' as value")
	assert(pos >= 1 and pos <= #self.values, "illegal position")
	self.values[pos] = newValue
	if pos > 1 then self:bubbleUp(pos) end
	if pos < #self.values then self:sinkDown(pos) end
end

local remove
--- Removes an element from the heap.
-- @function heap:remove
-- @param pos the position to remove
-- @return value, or nil if a bad `pos` value was provided
remove = function(self, pos)
	local last = #self.values
	if pos < 1 then
		return  -- bad pos

	elseif pos < last then
		local v = self.values[pos]
		self:swap(pos, last)
		self:erase(last)
		self:bubbleUp(pos)
		self:sinkDown(pos)
		return v

	elseif pos == last then
		local v = self.values[pos]
		self:erase(last)
		return v

	else
		return  -- bad pos: pos > last
	end
end

local insert
--- Inserts an element in the heap.
-- @function heap:insert
-- @param value the value used for sorting this element
-- @return nothing, or throws an error on bad input
insert = function(self, value) -- changed
	assert(value ~= nil, "cannot add 'nil' as value")
	local pos = #self.values + 1
	self.values[pos] = value
	self.position[value] = pos
	self:bubbleUp(pos)
end

local pop
--- Removes the top of the heap and returns it.
-- @function heap:pop
-- @return value at the top, or `nil` if there is none
pop = function(self)
	if self.values[1] ~= nil then
		return remove(self, 1)
	end
end

local peek
--- Returns the element at the top of the heap, without removing it.
-- @function heap:peek
-- @return value at the top, or `nil` if there is none
peek = function(self)
	return self.values[1]
end

local size
--- Returns the number of elements in the heap.
-- @function heap:size
-- @return number of elements
size = function(self)
	return #self.values
end

local function swap(heap, a, b) -- (pos_a and pos_b) -- changed
	heap.position[heap.values[a]], heap.position[heap.values[b]] = b, a
	heap.values[a], heap.values[b] = heap.values[b], heap.values[a]
end

local function erase(heap, pos) -- changed
	heap.position[heap.values[pos]] = nil
	heap.values[pos] = nil
end

--================================================================
-- new functions
-- changed: erase, swap, insert
local function append(self, obj)
	local pos = self.position[obj]
	if pos then
		self:update_old(pos, obj)
	else
		self:insert(obj)
	end
end

local function drop(self, obj)
	local pos = self.position[obj]
	if pos and self.values[pos] ~= nil then
		return remove(self, pos)
	end
end

local function update(self, obj)
	local pos = self.position[obj]
	if pos then
		self:update_old(pos, obj)
	else return nil end
end

local function first(self)
	return self.values[1]
end

--================================================================
--================================================================
-- TimerHeap heap creation
--================================================================

function TimerHeap(lt)
	-- if not lt then lt = function(a,b) return a[1] < b[1] end end
	local h = binaryHeap(swap, erase, lt)
	h.peek = peek
	h.pop = pop
	h.size = size
	h.remove = remove
	h.insert = insert -- changed
	h.update_old = update_old -- renamed

	h.position = {}
	h.append = append
	h.drop = drop
	h.update = update
	h.first = first
	return h
end
end
----------------------------------------------- Timer
if not TimerSystem then
local TimerHeap = TimerHeap
local TimeSystem = TimeSystem
assert(TimerHeap, TimeSystem)


----- class Timer
local index = {}

function index:every(delay, count, func, arg)
	if type(func) ~= 'function' then print('error') return end
	local Time = self.Time
	if delay <= 0 then delay = Time.ptime end
	self.delay = delay
	self.count = count
	self.func = func
	self.arg = arg
	self.start_time = Time.time
	self.end_time = Time.time + delay
	self.Heap:append(self)
	return self
end

function index:after(delay, func, arg)
	return self:every(delay , 1, func, arg)
end

function index:pause()
	return self.Heap:drop(self)
end

function index:remained()
	local time = self.end_time - self.Time.time
	return time > 0 and time or 0
end

local mt = {__index = index}
-----

local function lt(a,b) return a.end_time < b.end_time end

function TimerSystem(Time)
	Time = Time or TimeSystem()
	assert(Time)
	local Heap = TimerHeap(lt)

	local Timer = {}
	function Timer.new()
		local timer = {Time = Time, Heap = Heap}
		return setmetatable(timer, mt)
	end

	function Timer.after(...)
		return Timer.new():after(...)
	end

	function Timer.every(...)
		return Timer.new():every(...)
	end

	local function update_timer(self) -- timer
		self.count = self.count - 1
		if self.count > 0 then
			self.count = self.count - 1
			self.start_time = self.end_time
			self.end_time = self.end_time + self.delay
			Heap:append(self)
		end
		self.func(self, self.arg)
		if self.count <= 0 then Heap:pop() end
	end

	Time.subscribe(function(time)
		local timer = Heap:first()
		while timer and timer.end_time and timer.end_time <= time do
			update_timer(timer)
			timer = Heap:first()
		end
	end)
	return Timer
end
end
----------------------------------------------- TerrainSystem
if not TerrainSystem then
-- types - цепочки превращений земли до FOOTBALL_GRASS 
-- type - тип почвы
-- time - время перехода до следующей стадии
local EARTH = {
	{types = {'Ldrt', 'Ldrg', 'Fdrg'}, time = 3},
	{types = {'Lrok', 'Frok'}, time = 4},
}
local FOOTBALL_GRASS = {type = 'Lgrs', time = 5}
local GREEN_GRASS = {type = 'Lgrd', time = 6}
local YELLOW_GRASS = {type = 'Fgrd'}

local function FourCC(str)
	local n, len = 0, #str
	for i = len, 1, -1 do n = n + (str:byte(i,i) << 8*(len-i)) end -- shift by 0,8,16,24
	return n
end
local ALL_TYPES = {}
local function add_all_types(tbl) -- преобразовавыем строки в числа в бд
	if tbl.type then
		local str = tbl.type
		tbl.type = FourCC(tbl.type)
		ALL_TYPES[tbl.type] = str
	elseif tbl.types then
		for i=1, #tbl.types do
			local str = tbl.types[i]
			tbl.types[i] = FourCC(tbl.types[i])
			ALL_TYPES[tbl.types[i]] = str
		end
	else
		for _, dict in ipairs(tbl) do add_all_types(dict) end
	end
end
add_all_types{EARTH, FOOTBALL_GRASS, GREEN_GRASS, YELLOW_GRASS}

local function IsEarth(self,x,y)
	local typ = self.get_type(x,y)
	for _, chain in ipairs(EARTH) do
		for _, next_typ in ipairs(chain.types) do
			if next_typ == typ then return true end
		end
	end
end

local function IsYellowGrass(self,x,y)
	return self.get_type(x,y) == YELLOW_GRASS.type
end

local function IsGreenGrass(self,x,y)
	return self.get_type(x,y) == GREEN_GRASS.type
end

local function IsFootballGrass(self,x,y)
	return self.get_type(x,y) == FOOTBALL_GRASS.type
end

-- @param typ - optional - needed type
-- @return (next_type, time) or nil
local function next(self, x, y, typ)
	local typ = typ or self.get_type(x,y)
	local time, next_typ

	if not ALL_TYPES[typ] then return nil end
	if GREEN_GRASS.type == typ then
		time = GREEN_GRASS.time
		next_typ = YELLOW_GRASS.type
		return next_typ, time
	elseif FOOTBALL_GRASS.type == typ then
		time = FOOTBALL_GRASS.time
		next_typ = GREEN_GRASS.type
		return next_typ, time
	else
		for _, chain in ipairs(EARTH) do -- пробегаем по EARTH
			for i, next_typ in ipairs(chain.types) do
				if typ == next_typ then -- если совпал тип
					if i >= #chain.types then -- если последняя стадия
						next_typ = FOOTBALL_GRASS.type
					else
						next_typ = chain.types[i+1]
					end
					time = chain.time
					return next_typ, time
				end
			end
		end
	end
end

local update
local function save_point(self, x, y, time)
	local timer = self.array:get_xy(x,y)
	if not timer then
		timer = self.Timer.after(time, update, {self,x,y,1}) -- 1-speed
		-- timer = Timer.start(time, false, update, {self,x,y,1})
		self.array:set_xy(x,y, timer)
	else
		timer:after(time, update, {self,x,y,1})
	end
end

update = function(timer, arg)
	local self, x, y = arg[1], arg[2], arg[3]
	local typ, dtime = next(self, x, y)
	assert(typ)
	self.set_type(x,y, typ)
	typ, dtime = next(self, x, y, typ)
	-- print('next typ', ALL_TYPES[typ])
	if typ then
		timer:after(dtime, update, arg)
	else
		self.array:set_xy(x,y,nil)
	end
end

local index = {}
function index:start_tox(x,y) --> true or false
	if self.array:is_xy(x,y) then
		local typ, dtime = next(self, x, y)
		if not dtime then return false end
		save_point(self, x, y, dtime)
	else return false end
end

function index:stop_tox(x,y)
	local timer = self.array:get_xy(x,y)
	if timer then
		timer:pause()
		self.array:set_xy(x,y,nil)
	end
end

function index:modification(x,y, ratio) --> true or false
	local timer = self.array:get_xy(x,y)
	-- print('timer', timer, x, y)
	if timer then
		local speed = timer.arg[4] + ratio
		timer.arg[4] = speed
		local dtime = timer:remained()*(speed >= 0 and speed or 0)
		-- print('dtime', dtime)
		timer:after(dtime, timer.func, timer.arg)
		return true
	else return false end
end

function index:init()
	local rand, array = math.random, self.array
	local up_time = (array.maxIX*array.maxIY)/100
	for x, y, ix, iy in array:by_all_square() do
		local typ, dtime = next(self, x, y)
		if dtime then
			dtime = dtime+rand()*up_time
			save_point(self, x, y, dtime)
		end
	end
end

index.IsEarth = IsEarth
index.IsGreenGrass = IsGreenGrass
index.IsYellowGrass = IsYellowGrass
index.IsFootballGrass = IsFootballGrass

local mt = {__index = index}

-- @param Timer = require "Timer"
-- @param array = require "SquareArray"
-- @param get_type = function(real x, real y) --> terrain_type
-- @param set_type = function(real x, real y, terrain_type type)
function TerrainSystem(tbl)
	assert(tbl.Timer and tbl.array and tbl.get_type and tbl.set_type)
	local object = {
		Timer = tbl.Timer,
		get_type = tbl.get_type,
		set_type = tbl.set_type,
		array = tbl.array,
	}
	return setmetatable(object, mt)
end

end

----------------------------------------------- Пример использования TerrainSystem

print(1)
-- math.randomseed(GetRandomInt(0, 2147483648-1)) -- синхронизируем lua рандом
print(2)
local world_rect = GetWorldBounds()
local minX, maxX, minY, maxY = GetRectMinX(world_rect), GetRectMaxX(world_rect), GetRectMinY(world_rect), GetRectMaxY(world_rect)
local step = 128 -- шаг сетки
print(3)
local Timer = TimerSystem()
print(4)
local ts_array = SquareArray(minX, maxX, minY, maxY, step) -- квадратный массив для создания TerrainSystem
print(5)
local ts_set = function (x,y,typ) SetTerrainType(x,y,typ,math.random(4)-1,1,0) end
print(6)
local ts_get = GetTerrainType
print(7)
ter_sys = TerrainSystem{
	Timer = Timer,
	array = ts_array,
	get_type = ts_get,
	set_type = ts_set,
}
print(8)
-- функция конвертации строки-типа в число, для того чтобы показать пример
local function FourCC(str)
	local n, len = 0, #str
	for i = len, 1, -1 do n = n + (str:byte(i,i) << 8*(len-i)) end -- shift by 0,8,16,24
	return n
end
print(9)
-- создаем 3 участка
SetTerrainType(-200,200, FourCC('Ldrg'), 1, 3, 0)

SetTerrainType(200,-200, FourCC('Lrok'), 1, 3, 0)

SetTerrainType(-200,-200, FourCC('Ybtl'), 1, 3, 0)


-- print('ter_sys:IsEarth(-200, 200)', ter_sys:IsEarth(-200, 200))
-- print('ter_sys:IsYellowGrass(-200, 200)', ter_sys:IsYellowGrass(-200, 200))

-- запуск по всей карте
ter_sys:init() 


-- запуск в квадратном участке
-- for x = -384, 384, 128 do
-- 	for y = -384, 384, 128 do
-- 		ter_sys:start_tox(x, y)
-- 	end
-- end

-- Timer.after(1, function()
-- 	ter_sys:stop_tox(0,128)
-- 	ter_sys:modification(128,0, 5)
-- end)


    end
end
Загруженные файлы
2
17
2 года назад
2
Круто, но какое-то рандомное оно, вот бы в разные стороны разрасталось островками такими и каждый новый уровень от центра островка этого
По ТЗ там одновременно должно вырасти, рандома я добавил чтобы игра не провисла)

Обновление
  • Зафиксирован вылет на reforget из-за math.randomseed()
  • Для reforget теперь отдельный код, добавлена карта от МрачныйВорон
  • Для 1.26 файлы Scripts\blizzard.lua и Scripts\common.lua включены в карту сразу

Lord_Teo, класс. Для видео используй формат .mp4. Для того чтобы убедиться, что видео поддерживается сайтом, есть кнопка Предпросмотр в ресурсах и комментариях
1
26
2 года назад
Отредактирован Lord_Teo
1
Lord_Teo, класс. Для видео используй формат .mp4. Для того чтобы убедиться, что видео поддерживается сайтом, есть кнопка Предпросмотр в ресурсах и комментариях
Спасибо. Я в курсе про предосмотр, но я не знал как сделать, чтобы видео было видно😄
Загруженные файлы
Этот комментарий удален
3
17
2 года назад
3
  • Обновил библиотеки внутри работы
  • Поправил подсветку синтаксиса в шапке
2
27
7 месяцев назад
2
Снова просматривал эту наработку от скуки. Я вот думаю, что почва так не растет. Она оч быстро растет у вас, по видео, но в качестве ознакомительного проекта сойдет.
Вот пример в реале, я на даче заметил, что там где песок, травы не видно. Но стоит туда чернозем завести, вода сделает свое дело, и со временем там зарастает. Стоит туда куслк навоза кинуть, и там уже маленький островок из травы. Если косишь траву, то вся эта скошенная трава становится новым удобрением для будущей травы, так и расширчется по тихоньку. И так далее.
0
17
7 месяцев назад
0
Она оч быстро растет
Это ускоренный вариант для видео
Чтобы оставить комментарий, пожалуйста, войдите на сайт.