Добавлен , опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
Lua
Тип:
Наработка
Версия Warcraft:
1.26а + reforged
Позволяет игрокам захватывать и передавать участки земли
Особенности
  • Возможность устанавливать размер сетки
  • Игрок может захватывать участки земли
  • Игрок может передавать участок земли другим игрокам
  • Игрок не может захватить занятую землю
  • Игрок путем "покупки" не может захватывать землю исключительно по периметру (создавать дыры)
API
local map = Territory(minX, minY, maxX, maxY, step)
-- @param playerId - int id
map:can_build(playerId, x0, y0, x1, y1) --> true/false --  x0, y0, x1, y1 - rect
map:build_land:(playerId, x0, y0, x1, y1) --> true/false -- можно только возле созданных участков
map:sell_land(sellerId, buyerId, x0, y0, x1, y1) -- передать участок, только если принадлежит вам
map:new_village(playerId, x0, y0, radius) -- создать базовый участок
map:player_points(playerId) -- возвращает структуру с перечислением всех регионов (area) и их точками. см. пример
По кнопке скачать находится архив с исходниками и примером для 1.26 и для reforged
Благодарность МрачныйВорон за спонсирование работы
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
32
2 года назад
0
Ох уж это луа на 1.26
2
27
2 года назад
Отредактирован MpW
2
у меня есть полный код с картой TerrainSystem, но она устарела. Vlod внес какие то правки.
Прикрепляю старые наработки на jass - A3,A5, или вернее недоработки на 1.26. Кому надо, к сожалению, не могу карту открыть. Все что надо перенес на луа. Но может пригодиться мне.

Вот и вторая часть. Улучшенная. Проверили работу на рефе
код Territory and Terrain
код территории и терраин конфликтовался. Так, что пришлось объединить
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 minmax(a, b)
	if a <= b then return a, b
	else return b, a end
end

local function is_x(self, x)
	return self.minX <= x and self.maxX >= x
end

local function is_y(self, y)
	return self.minY <= y and self.maxY >= y
end

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

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

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

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

local function norm_xx(self, minX, maxX)
	minX, maxX = minmax(minX, maxX)
	if maxX < self.minX or minX > self.maxX then return nil end
	if minX < self.minX then minX = self.minX end
	if maxX > self.maxX then maxX = self.maxX end
	return minX, maxX
end

local function norm_yy(self, minY, maxY)
	minY, maxY = minmax(minY, maxY)
	if maxY < self.minY or minY > self.maxY then return nil end
	if minY < self.minY then minY = self.minY end
	if maxY > self.maxY then maxY = self.maxY end
	return minY, maxY
end

local function norm_ixix(self, minIX, maxIX)
	minIX, maxIX = minmax(minIX, maxIX)
	if maxIX < self.minIX or minIX > self.maxIX then return nil end
	if minIX < self.minIX then minIX = self.minIX end
	if maxIX > self.maxIX then maxIX = self.maxIX end
	return minIX, maxIX
end

local function norm_iyiy(self, minIY, maxIY)
	minIY, maxIY = minmax(minIY, maxIY)
	if maxIY < self.minIY or minIY > self.maxIY then return nil end
	if minIY < self.minIY then minIY = self.minIY end
	if maxIY > self.maxIY then maxIY = self.maxIY end
	return minIY, maxIY
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:rect_to_indexes(x0, y0, x1, y1)
	x0, x1 = norm_xx(self, x0, x1)
	y0, y1 = norm_yy(self, y0, y1)
	if not (x0 and y0) then return nil end
	x0, y0 = self:xy_to_ixiy(x0, y0)
	return x0, y0, self:xy_to_ixiy(x1, y1)
end

function index:xy_to_ixiy(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:xy_to_ixiy(x, y)
	return ix and iy and self.grid[iy][ix] or nil
end

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

function index:get_ixiy(ix, iy)
	ix, iy = norm_ix(self, ix), norm_iy(self, iy)
	return ix and iy and self.grid[iy][ix] or nil
end

function index:set_ixiy(ix, iy, val)
	ix, iy = norm_ix(self, ix), norm_iy(self, iy)
	if ix and iy then self.grid[iy][ix] = val end
end

function index:id_xy(x, y)
	local ix, iy = self:xy_to_ixiy(x, y)
	return ix and iy and iy*self.maxIX+ix or nil
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

function index:by_square(minX, minY, maxX, maxY)
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	local minIX, maxIX = norm_xx(self, minX, maxX)
	local minIY, maxIY = norm_yy(self, minY, maxY)
	if not (minIX and minIY) then return nil end
	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

function index:by_isquare(minIX, minIY, maxIX, maxIY)
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	minIX, maxIX = norm_ixix(self, minIX, maxIX)
	minIY, maxIY = norm_iyiy(self, minIY, maxIY)
	if not (minIX and minIY) then return nil end
	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

function index:by_circle(x0, y0, rds)
	rds = math.abs(rds)
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	local minX, maxX = x0-rds, x0+rds
	local minY, maxY = y0-rds, y0+rds
	local t_minX = minX - step

	local opt_rds_2 = rds*rds
	local opt_y_2 = (minY-y0)*(minY-y0)
	return function()
		while true do
			t_minX = t_minX + step
			if t_minX > maxX then
				t_minX = minX
				while true do
					minY = minY + step
					if minY > maxY then return end
					if is_y(self, minY) then
						opt_y_2 = (minY-y0)*(minY-y0)
						break
					end
				end
			end
			if is_x(self, t_minX) and opt_y_2+(t_minX-x0)*(t_minX-x0) <= opt_rds_2 then
				return t_minX, minY, round((t_minX+offsetX)/step), round((minY+offsetY)/step)
			end
		end
	end
end


local mt = {__index = index}

function SquareArray(minX, minY, maxX, maxY, step)
	minX, maxX = minmax(minX, maxX)
	minY, maxY = minmax(minY, maxY)
	print(7.1, minX < maxX and minY < maxY and step > 0)
	assert(minX < maxX and minY < maxY and step > 0)
	print(7.2, step > 0 and maxX-minX > step and maxY-minY > step)
	assert(step > 0 and maxX-minX > step and maxY-minY > step)
	print(7.3)
	local maxIX = floor((maxX-minX)/step)
	local maxIY = floor((maxY-minY)/step)
	local grid = {}
	local object = {
		minX=minX, maxX=maxIX*step, minY=minY, maxY=maxIY*step, step=step,
		offsetY = -minY, offsetX = -minX, players = {},
		minIX = 0, minIY = 0, maxIX = maxIX, maxIY = maxIY,
		grid = grid,
	}
	for i=0, object.maxIY do grid[i] = {} end
	return setmetatable(object, mt)
end
end
print(1)
----------------------------------------------- Time
if not TimeSystem then
function TimeSystem(ptime)
	local list = {}
	local ht = {}
	local ptime = ptime or 0.03125
	local tick = 0
	local isRun = true

	local Time = {
		ptime = ptime,
		time = 0,
	}

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

	function Time.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

	function Time.resume()
		while isRun do
			update()
		end
	end

	function Time.start()
		tick = 0
		Time.resume()
	end

	function Time.stop()
		isRun = false
	end

	-- connect API game
	TimerStart(CreateTimer(), ptime, true, update)
	return Time
end
end
print(2)
----------------------------------------------- TimerHeap
-- https://github.com/Tieske/binaryheap.lua/blob/master/src/binaryheap.lua
if not TimerHeap then
local assert = assert
local floor = math.floor
--================================================================
-- 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

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


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

function index:every(delay, count, func, arg)
	if type(func) ~= 'function' then print('error') return end
	if count <= 0 then count = 1 end
	if delay <= 0 then delay = self.Time.ptime end
	self.delay = delay
	self.count = count
	self.func = func
	self.arg = arg
	self.start_time = self.Time.time
	self.end_time = self.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()
	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

	Time.subscribe(function(time)
		local timer = Heap:first()
		while timer and timer.end_time <= time do
			timer.count = timer.count - 1
			if timer.count > 0 then
				timer.start_time = time
				timer.end_time = time + timer.delay
				Heap:append(timer)
				timer.func(timer, timer.arg)
			else
				timer.func(timer, timer.arg)
				if timer.count <= 0 then Heap:pop() end
			end
			timer = Heap:first()
		end
	end)
	return Timer
end
end
print(4)
----------------------------------------------- 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
print(5)
----------------------------------------------- Territory
if not Territory then

local table_empty = {}

local function minmax(a, b)
	if a <= b then return a, b
	else return b, a end
end

local function area_generator()
	local i = 0 return function() i = i + 1 return i end
end

-- ok
local function is_player(self, playerId)
	return self.players[playerId] and playerId
end
-- ok
local function cell_create(playerId, areaId)
	return {pid = playerId, aid = areaId}
end
-- ok
local function is_cell_player(cell, playerId)
	return cell and cell.pid==playerId
end
local function player_create(self, playerId)
	if self.players[playerId] then return end
	local pl = {
		pid = playerId,
		areas = {},
	}
	self.players[playerId] = pl
end

local function area_create(self, tbl)
	if not (tbl.x and tbl.y and tbl.radius
		and tbl.pid and is_player(self, tbl.pid)
		and tbl.count
		and tbl.minIX and tbl.maxIX and tbl.minIY and tbl.maxIY) then
		-- print('area_create error')
		return nil
	end
	local area = {
		aid = self.gen(),
		pid = tbl.pid,
		x = tbl.x,
		y = tbl.y,
		radius = tbl.radius,
		count = tbl.count,
		minIX = tbl.minIX,
		maxIX = tbl.maxIX,
		minIY = tbl.minIY,
		maxIY = tbl.maxIY,
	}
	self.players[area.pid].areas[area.aid] = area
	self.areas[area.aid] = area
	return area
end

local function area_add_cell(area, cell, ix, iy)
	area.count = area.count + 1
	if iy < area.minIY then area.minIY = iy end
	if iy > area.maxIY then area.maxIY = iy end
	if ix < area.minIX then area.minIX = ix end
	if ix > area.maxIX then area.maxIX = ix end
	-- print('cell', 'area '..area.aid, 'ix '..ix, 'iy '..iy)
end

local function area_delete(self, areaId)
	-- print('area_delete', 'start')
	local a = self.areas[areaId] -- area
	if not a or a.count > 0 then return true end
	-- for _, _, ix, iy in self.array:by_isquare(a.minIX, a.minIY, a.maxIX, a.maxIY) do
	-- 	local cell = array:get_ixiy(ix, iy)
	-- 	if cell and cell.aid == areaId then array:set_ixiy(ix, iy, nil)
	-- end
	self.players[a.pid].areas[areaId] = nil
	self.areas[areaId] = nil
	-- print('area_delete', 'end')
	return true
end
-- ok
local function update_area_size(self, areaId)
	local area, array = self.areas[areaId], self.array
	local count = 0
	local minIX, maxIX, minIY, maxIY = area.maxIX, area.minIX, area.maxIY, area.minIY
	for _, _, ix, iy in array:by_isquare(minIX, minIY, maxIX, maxIY) do
		local cell = array:get_ixiy(ix, iy)
		if cell and cell.aid == areaId then
			if iy < minIY then minIY = iy end
			if iy > maxIY then maxIY = iy end
			if ix < minIX then minIX = ix end
			if ix > maxIX then maxIX = ix end
			count = count + 1
		end
	end
	area.minIX, area.maxIX, area.minIY, area.maxIY = minIX, maxIX, minIY, maxIY
	area.count = count
end
-- ok
local function update_reduced_area(self, areaId)
	local area = self.areas[areaId]
	if not area then return end
	if area.count <= 0 then
		return area_delete(self, areaId)
	end
	update_area_size(self, areaId)
end
 -- ok
local function player_in_rect_one_more(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		local cell = array:get_ixiy(ix, iy) -- хотя бы 1 свой кусочек
		if is_cell_player(cell, playerId) then return true end
	end
	return false
end
 -- ok
local function empty_in_rect_one_more(self, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		local cell = array:get_ixiy(ix, iy)
		if not cell then return true end
	end
	return false
end
-- ok
local function rect_build(self, playerId, areaId, ix0, iy0, ix1, iy1)
	local array = self.array
	local area = self.areas[areaId]
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		if not array:get_ixiy(ix, iy) then
			local cell = cell_create(playerId, areaId)
			array:set_ixiy(ix, iy, cell)
			area_add_cell(area, cell, ix, iy)
		end
	end
end
-- ok
local function rect_transfer(self, sellerId, buyerId, areaId, ix0, iy0, ix1, iy1)
	local array, touched_areas = self.array, {}
	local area = self.areas[areaId]
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		local cell = array:get_ixiy(ix, iy)
		if cell then
			if cell.pid == sellerId then
				local aid = cell.aid
				area.count = area.count - 1
				touched_areas[aid] = true
				cell.pid, cell.aid = buyerId, areaId
				area_add_cell(area, cell, ix, iy)
			end
		else
			cell = cell_create(buyerId, areaId)
			array:set_ixiy(ix, iy, cell)
			area_add_cell(area, cell, ix, iy)
		end
	end
	for aid, _ in pairs(touched_areas) do
		update_reduced_area(self, aid)
	end
end
-- ok
local function near_area_to_rect(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0-1, iy0, ix0-1, iy1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy1+1, ix1, iy1+1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	for _, _, ix, iy in array:by_isquare(ix1+1, iy0, ix1+1, iy1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy0-1, ix1, iy0-1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	return nil
end
-- ok
local function is_rect_surrounded_player(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0-1, iy0, ix0-1, iy1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy1+1, ix1, iy1+1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	for _, _, ix, iy in array:by_isquare(ix1+1, iy0, ix1+1, iy1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy0-1, ix1, iy0-1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	return true
end
-- ok
local function max_area_player_rect(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	local max, tbl, areaId = 0, {}
	for _, _, ix, iy in array:by_isquare(ix0, iy0, ix1, iy1) do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then
			local aid = cell.aid
			tbl[aid] = tbl[aid] and tbl[aid]+1 or 1
		end
	end
	for aid, num in pairs(tbl) do
		if max < num then max, areaId = num, aid end
	end
	return areaId
end

local function mark_border(self, pid, ix0, iy0, ix1, iy1)
	local array = self.array
	local start, first = nil, true -- first cell
	local count, mark, rect  = 0, {}, {}
	-- print('mark_border', ix0, iy0, ix1, iy1)
	for _, _, ix, iy in array:by_isquare(ix0-1, iy0, ix0-1, iy1) or table_empty do
		local cell = array:get_ixiy(ix0, iy) -- left
		mark[iy] = {}
		rect[iy] = {}
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) then
			mark[iy][ix0] = true
			count = count + 1
			if first then start = {2, ix0, iy} first = nil end
		end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy1+1, ix1, iy1+1) or table_empty do
		local cell = array:get_ixiy(ix, iy1) -- top
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) and not mark[iy1][ix] then
			mark[iy1][ix] = true
			count = count + 1
			if first then start = {3, ix, iy1} first = nil end
		end
	end
	for _, _, ix, iy in array:by_isquare(ix1+1, iy0, ix1+1, iy1) or table_empty do
		local cell = array:get_ixiy(ix1, iy) -- right
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) and not mark[iy][ix1] then
			mark[iy][ix1] = true
			count = count + 1
			if first then start = {4, ix1, iy} first = nil end
		end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy0-1, ix1, iy0-1) or table_empty do
		local cell = array:get_ixiy(ix, iy0) -- bot
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) and not mark[iy0][ix] then
			mark[iy0][ix] = true
			count = count + 1
			if first then start = {1, ix, iy0} first = nil end
		end
	end
	-- запонение прямоугольника новой области
	for _, _, ix, iy in array:by_isquare(ix0, iy0, ix1, iy1) do
		local cell = array:get_ixiy(ix, iy)
		if not cell then rect[iy][ix] = true end
	end

	if start then -- значит забагаюзали, убирается дырка
		return rect, mark, count, start[1], start[2], start[3]
	else
		return rect, mark, count
	end
end

local function stbl(tbl, x, y) return tbl[y] and tbl[y][x] end
local function bypass_border(self, pid, ix0, iy0, ix1, iy1)
	local array = self.array
	local rect, mark, count, state, x0, y0 = mark_border(self, pid, ix0, iy0, ix1, iy1)
	if not state then return true end
	local tx, ty = x0, y0
	local opt = 0
	-- print('x0, y0',x0, y0)
	repeat
		if stbl(mark, tx, ty) then
			mark[ty][tx] = nil
			count = count - 1
		end
		while true do
			opt = opt + 1
			if opt > 400 then print('bypass_border error opt') return end
			-- print('tx '..tx, 'tx '..ty, 'state '..state)
			if state == 1 then
				local cell = array:get_ixiy(tx-1, ty-1)
				if is_cell_player(cell, pid) or stbl(rect, tx-1, ty-1) then
					tx, ty, state = tx-1, ty-1, 4 break
				else
					cell = array:get_ixiy(tx-1, ty)
					if is_cell_player(cell, pid) or stbl(rect, tx-1, ty) then
						tx = tx-1 break
					else
						state = 2
					end
				end
			elseif state == 2 then
				local cell = array:get_ixiy(tx-1, ty+1)
				if is_cell_player(cell, pid) or stbl(rect, tx-1, ty+1) then
					tx, ty, state = tx-1, ty+1, 1 break
				else
					cell = array:get_ixiy(tx, ty+1)
					if is_cell_player(cell, pid) or stbl(rect, tx, ty+1) then
						ty = ty+1 break
					else
						state = 3
					end
				end
			elseif state == 3 then
				local cell = array:get_ixiy(tx+1, ty+1)
				if is_cell_player(cell, pid) or stbl(rect, tx+1, ty+1) then
					tx, ty, state = tx+1, ty+1, 2 break
				else
					cell = array:get_ixiy(tx+1, ty)
					if is_cell_player(cell, pid) or stbl(rect, tx+1, ty) then
						tx = tx+1 break
					else
						state = 4
					end
				end
			elseif state == 4 then
				local cell = array:get_ixiy(tx+1, ty-1)
				if is_cell_player(cell, pid) or stbl(rect, tx+1, ty-1) then
					tx, ty, state = tx+1, ty-1, 3 break
				else
					cell = array:get_ixiy(tx, ty-1)
					if is_cell_player(cell, pid) or stbl(rect, tx, ty-1) then
						ty = ty-1 break
					else
						state = 1
					end
				end
			end
		end
	until tx==x0 and ty==y0
	-- print('count', count)
	return count <= 0
end

-- basic
local function can_build(self, playerId, x0, y0, x1, y1)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x0,y0,x1,y1)
	if not is_player(self, playerId)
		or not minIX
		or not empty_in_rect_one_more(self, minIX, minIY, maxIX, maxIY) then
		return false
	end
	local areaId = max_area_player_rect(self, playerId, minIX, minIY, maxIX, maxIY)
				or near_area_to_rect(self, playerId, minIX, minIY, maxIX, maxIY)
	-- print('can_build areaId', areaId)
	if not areaId then return false end
	if not bypass_border(self, playerId, minIX, minIY, maxIX, maxIY) then
		-- print('bypass_border error 2')
		return false
	end
	return true, areaId
end


-- basic
local function build_land(self, playerId, x0, y0, x1, y1)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x0,y0,x1,y1)
	if not (minIX and is_player(self, playerId)) then return false end
	local can, areaId = self:can_build(playerId, x0, y0, x1, y1)
	if not can then return false end
	rect_build(self, playerId, areaId, minIX, minIY, maxIX, maxIY)
	return true
end


-- basic
local function sell_land(self, sellerId, buyerId, x0, y0, x1, y1)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x0,y0,x1,y1)
	if not (minIX and is_player(self, sellerId) and is_player(self, buyerId))
		or is_rect_surrounded_player(self, sellerId, minIX, minIY, maxIX, maxIY)
		or not player_in_rect_one_more(self, sellerId, minIX, minIY, maxIX, maxIY) then
		return false
	end
	local areaId = near_area_to_rect(self, buyerId, minIX, minIY, maxIX, maxIY)
	if not areaId then
		areaId = area_create(self, {
			pid = buyerId,
			x = (x0+x1)*0.5,
			y = (y0+y1)*0.5,
			radius = 0,
			count = 0,
			minIX = minIX,
			maxIX = maxIX,
			minIY = minIY,
			maxIY = maxIY,
		}).aid
	end
	rect_transfer(self, sellerId, buyerId, areaId, minIX, minIY, maxIX, maxIY)
	return true
end

local function can_new_village(self, playerId, x0, y0, radius)
	-- ищем перекрывающиеся деревни
	for aId, tbl in pairs(self.areas) do
		local dist = (tbl.x-x0)*(tbl.x-x0)+(tbl.y-y0)*(tbl.y-y0)
		local rds = radius+tbl.radius
		if dist < rds*rds then
			-- print('dist error')
			return false
		end
	end
	-- смотрим не заняты ли клетки
	local array = self.array
	for _, _, ix, iy in array:by_circle(x0, y0, radius) do
		local cell = array:get_ixiy(ix, iy)
		if cell and cell.pid~=playerId then
			-- print('array error')
			return false
		end
	end
	return true
end

-- basic
local function new_village(self, playerId, x, y, rds)
	if not (playerId and x and y and rds)
		or not can_new_village(self, playerId, x, y, rds) then
		return false
	end
	player_create(self, playerId)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x-rds,y-rds,x+rds,y+rds)
	local area = area_create(self, {
		x = x,
		y = y,
		radius = rds,
		pid = playerId,
		count = 0,
		minIX = minIX,
		maxIX = maxIX,
		minIY = minIY,
		maxIY = maxIY,
	})
	local array, areaId = self.array, area.aid
	for _, _, ix, iy in array:by_circle(x, y, rds) do
		-- print('v',ix,iy)
		local cell = cell_create(playerId, areaId)
		array:set_ixiy(ix, iy, cell)
		area_add_cell(area, cell, ix, iy)
	end
	if area.count <= 0 then
		print('error new_village')
		area_delete(self, area) --
		return false
	end
	-- print('areaId', areaId)
	return true
end

local function area_points(self, areaId)
	local array, a = self.array, self.areas[areaId]
	if not a then return nil end
	local couples = {}
	for x, y, ix, iy in array:by_isquare(a.minIX, a.minIY, a.maxIX, a.maxIY) do
		local cell = array:get_ixiy(ix, iy)
		if cell and cell.aid == areaId then
			table.insert(couples, x)
			table.insert(couples, y)
		end
	end
	return couples
end

local function for_sort(a,b) return a.id < b.id end
local function player_points(self, playerId)
	if not is_player(self, playerId) then return nil end
	local areas = self.players[playerId].areas
	local list = {}
	for areaId, area in pairs(areas) do -- desync
		table.insert(list, {id=area.aid, data = area_points(self, areaId)})
	end
	table.sort(list, for_sort)
	return list
end

local index = {
	can_build = can_build,
	build_land = build_land,
	sell_land = sell_land,
	new_village = new_village,
	player_points = player_points,
}
-- index.sell_land = sell_land
-- index.new_village = new_village

local mt = {__index = index}

function Territory(minX, minY, maxX, maxY, step)
	local array = SquareArray(minX, minY, maxX, maxY, step)
	local object = {
		step = step, offsetY = -minY, offsetX = -minX,
		array = array,
		areas = {}, players = {},
		gen = area_generator()
	}
	return setmetatable(object, mt)
end
end

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

-- math.randomseed(GetRandomInt(0, 2147483648-1)) -- синхронизируем lua рандом

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(6)
local Timer = TimerSystem()
print(7)
print(minX, maxY, minY, maxX, step)
local ts_array = SquareArray(minX, minY, maxX, maxY, step) -- квадратный массив для создания TerrainSystem
print(8)
local ts_set = function (x,y,typ) SetTerrainType(x,y,typ,math.random(4)-1,1,0) end
local ts_get = GetTerrainType
print(9)
ter_sys = TerrainSystem{
	Timer = Timer,
	array = ts_array,
	get_type = ts_get,
	set_type = ts_set,
}
print(10)
-- функция конвертации строки-типа в число, для того чтобы показать пример
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)


-- print('ter_sys:IsEarth(-200, 200)', ter_sys:IsEarth(-200, 200))
-- print('ter_sys:IsYellowGrass(-200, 200)', ter_sys:IsYellowGrass(-200, 200))
print(11)
-- запуск по всей карте
ter_sys:init()
----------------------------------------------- Пример использования Territory
print(12)
do
local world_rect = GetWorldBounds()
-- получаем все значение карты
local minX, maxX, minY, maxY = GetRectMinX(world_rect), GetRectMaxX(world_rect), GetRectMinY(world_rect), GetRectMaxY(world_rect)
local step = 64 -- шаг сетки
print(13)
print(minX, minY, maxX, maxY, step, Territory)
local map = Territory(minX, minY, maxX, maxY, step)
local home_radius = 150 -- радиус деревни
local buy_radius = step/2 -- радиус покупки земли
print(14)

-------------------------- пример - отображение в игре
local player_units = {[1]={},[2]={}} -- храним юнитов отображения для plyerId-1 и plyerId-2

-- обновляем ландшафт в точке (пример)
local function update_land_ex(pId, x, y)
	local red = pId==1 and 255 or 150
	local blue = pId==2 and 255 or 150
	local green = 150
	local alpha = 100
	local size = 64 -- размер юнита
	local scale = step/size
	-- 1747988528 это его UID
	local uni = CreateUnit(Player(pId), 1747988528, x, y, 0)
	SetUnitScale(uni, scale, scale, scale)
	SetUnitVertexColor(uni, red, blue, green, alpha)
	return uni
end

-- обновляем отображения для playerId
local function update_land(playerId)
	local tbl = {}
	local pl_areas = map:player_points(playerId)
	for _, area in ipairs(pl_areas) do -- идем по всем областям (area) игрока (playerId)
		area = area.data
		for i=1, #area, 2 do
			-- area[i], area[i+1] это x и y
			tbl[65536*area[i+1]+area[i]] = {area[i], area[i+1]}
		end
	end
	local dict = player_units[playerId]
	--// десинх
	for k, uni in pairs(dict) do if not tbl[k] then RemoveUnit(uni) dict[k] = nil end end -- тут удаляю юнитов которые больше не нужно отображать (квадратики)
	for k, couple in pairs(tbl) do if not dict[k] then dict[k] = update_land_ex(playerId, couple[1], couple[2]) end end -- тут дорисовываю новых юнитов
	--//
end
--------------------------
print(15)
-- Триггер который регистрирует нажатия на способности
-- 1 и 2 это тестовые plyerId
local trig = CreateTrigger()
TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
TriggerAddAction( trig, function()
	local aid, tx, ty = GetSpellAbilityId(), GetSpellTargetX(), GetSpellTargetY()
	local basic = 1093677104 -- это просто id способности
	local result
	print('tx,ty',tx,ty)
	if aid==basic then -- create 1
		print('create 1')
		result = map:new_village(1, tx, ty, home_radius, step)
		if result then update_land(1) end
		print(result)
	elseif aid==basic+1 then -- create 2
		print('create 2')
		result = map:new_village(2, tx, ty, home_radius, step)
		if result then update_land(2) end
		print(result)
	elseif aid==basic+2 then -- buy 1
		print('buy 1')
		result = map:build_land(1, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(1) end
		print(result)
	elseif aid==basic+3 then -- buy 2
		print('buy 2')
		result = map:build_land(2, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(2) end
		print(result)
	elseif aid==basic+4 then -- send 1 -> 2
		print('send 1 -> 2')
		result = map:sell_land(1,2, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(1) update_land(2) end
		print(result)
	elseif aid==basic+5 then -- send 2 -> 1
		print('send 2 -> 1')
		result = map:sell_land(2,1, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(1) update_land(2) end
		print(result)
	end
end)

end

	end
end
Only Territory
а здесь пример без террадеформирования Terrain.
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 minmax(a, b)
	if a <= b then return a, b
	else return b, a end
end

local function is_x(self, x)
	return self.minX <= x and self.maxX >= x
end

local function is_y(self, y)
	return self.minY <= y and self.maxY >= y
end

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

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

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

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

local function norm_xx(self, minX, maxX)
	minX, maxX = minmax(minX, maxX)
	if maxX < self.minX or minX > self.maxX then return nil end
	if minX < self.minX then minX = self.minX end
	if maxX > self.maxX then maxX = self.maxX end
	return minX, maxX
end

local function norm_yy(self, minY, maxY)
	minY, maxY = minmax(minY, maxY)
	if maxY < self.minY or minY > self.maxY then return nil end
	if minY < self.minY then minY = self.minY end
	if maxY > self.maxY then maxY = self.maxY end
	return minY, maxY
end

local function norm_ixix(self, minIX, maxIX)
	minIX, maxIX = minmax(minIX, maxIX)
	if maxIX < self.minIX or minIX > self.maxIX then return nil end
	if minIX < self.minIX then minIX = self.minIX end
	if maxIX > self.maxIX then maxIX = self.maxIX end
	return minIX, maxIX
end

local function norm_iyiy(self, minIY, maxIY)
	minIY, maxIY = minmax(minIY, maxIY)
	if maxIY < self.minIY or minIY > self.maxIY then return nil end
	if minIY < self.minIY then minIY = self.minIY end
	if maxIY > self.maxIY then maxIY = self.maxIY end
	return minIY, maxIY
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:rect_to_indexes(x0, y0, x1, y1)
	x0, x1 = norm_xx(self, x0, x1)
	y0, y1 = norm_yy(self, y0, y1)
	if not (x0 and y0) then return nil end
	x0, y0 = self:xy_to_ixiy(x0, y0)
	return x0, y0, self:xy_to_ixiy(x1, y1)
end

function index:xy_to_ixiy(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:xy_to_ixiy(x, y)
	return ix and iy and self.grid[iy][ix] or nil
end

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

function index:get_ixiy(ix, iy)
	ix, iy = norm_ix(self, ix), norm_iy(self, iy)
	return ix and iy and self.grid[iy][ix] or nil
end

function index:set_ixiy(ix, iy, val)
	ix, iy = norm_ix(self, ix), norm_iy(self, iy)
	if ix and iy then self.grid[iy][ix] = val end
end

function index:id_xy(x, y)
	local ix, iy = self:xy_to_ixiy(x, y)
	return ix and iy and iy*self.maxIX+ix or nil
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

function index:by_square(minX, minY, maxX, maxY)
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	local minIX, maxIX = norm_xx(self, minX, maxX)
	local minIY, maxIY = norm_yy(self, minY, maxY)
	if not (minIX and minIY) then return nil end
	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

function index:by_isquare(minIX, minIY, maxIX, maxIY)
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	minIX, maxIX = norm_ixix(self, minIX, maxIX)
	minIY, maxIY = norm_iyiy(self, minIY, maxIY)
	if not (minIX and minIY) then return nil end
	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

function index:by_circle(x0, y0, rds)
	rds = math.abs(rds)
	local offsetX, offsetY, step = self.offsetX, self.offsetY, self.step
	local minX, maxX = x0-rds, x0+rds
	local minY, maxY = y0-rds, y0+rds
	local t_minX = minX - step

	local opt_rds_2 = rds*rds
	local opt_y_2 = (minY-y0)*(minY-y0)
	return function()
		while true do
			t_minX = t_minX + step
			if t_minX > maxX then
				t_minX = minX
				while true do
					minY = minY + step
					if minY > maxY then return end
					if is_y(self, minY) then
						opt_y_2 = (minY-y0)*(minY-y0)
						break
					end
				end
			end
			if is_x(self, t_minX) and opt_y_2+(t_minX-x0)*(t_minX-x0) <= opt_rds_2 then
				return t_minX, minY, round((t_minX+offsetX)/step), round((minY+offsetY)/step)
			end
		end
	end
end


local mt = {__index = index}

function SquareArray(minX, minY, maxX, maxY, step)
	minX, maxX = minmax(minX, maxX)
	minY, maxY = minmax(minY, maxY)
	assert(minX < maxX and minY < maxY and step > 0)
	assert(step > 0 and maxX-minX > step and maxY-minY > step)
	local maxIX = floor((maxX-minX)/step)
	local maxIY = floor((maxY-minY)/step)
	local grid = {}
	local object = {
		minX=minX, maxX=maxIX*step, minY=minY, maxY=maxIY*step, step=step,
		offsetY = -minY, offsetX = -minX, players = {},
		minIX = 0, minIY = 0, maxIX = maxIX, maxIY = maxIY,
		grid = grid,
	}
	for i=0, object.maxIY do grid[i] = {} end
	return setmetatable(object, mt)
end
end

----------------------------------------------- TerrainSystem
if not Territory then
assert(SquareArray)

local table_empty = {}

local function minmax(a, b)
	if a <= b then return a, b
	else return b, a end
end

local function area_generator()
	local i = 0 return function() i = i + 1 return i end
end

-- ok
local function is_player(self, playerId)
	return self.players[playerId] and playerId
end
-- ok
local function cell_create(playerId, areaId)
	return {pid = playerId, aid = areaId}
end
-- ok
local function is_cell_player(cell, playerId)
	return cell and cell.pid==playerId
end
local function player_create(self, playerId)
	if self.players[playerId] then return end
	local pl = {
		pid = playerId,
		areas = {},
	}
	self.players[playerId] = pl
end

local function area_create(self, tbl)
	if not (tbl.x and tbl.y and tbl.radius
		and tbl.pid and is_player(self, tbl.pid)
		and tbl.count
		and tbl.minIX and tbl.maxIX and tbl.minIY and tbl.maxIY) then
		-- print('area_create error')
		return nil
	end
	local area = {
		aid = self.gen(),
		pid = tbl.pid,
		x = tbl.x,
		y = tbl.y,
		radius = tbl.radius,
		count = tbl.count,
		minIX = tbl.minIX,
		maxIX = tbl.maxIX,
		minIY = tbl.minIY,
		maxIY = tbl.maxIY,
	}
	self.players[area.pid].areas[area.aid] = area
	self.areas[area.aid] = area
	return area
end

local function area_add_cell(area, cell, ix, iy)
	area.count = area.count + 1
	if iy < area.minIY then area.minIY = iy end
	if iy > area.maxIY then area.maxIY = iy end
	if ix < area.minIX then area.minIX = ix end
	if ix > area.maxIX then area.maxIX = ix end
	-- print('cell', 'area '..area.aid, 'ix '..ix, 'iy '..iy)
end

local function area_delete(self, areaId)
	-- print('area_delete', 'start')
	local a = self.areas[areaId] -- area
	if not a or a.count > 0 then return true end
	-- for _, _, ix, iy in self.array:by_isquare(a.minIX, a.minIY, a.maxIX, a.maxIY) do
	-- 	local cell = array:get_ixiy(ix, iy)
	-- 	if cell and cell.aid == areaId then array:set_ixiy(ix, iy, nil)
	-- end
	self.players[a.pid].areas[areaId] = nil
	self.areas[areaId] = nil
	-- print('area_delete', 'end')
	return true
end
-- ok
local function update_area_size(self, areaId)
	local area, array = self.areas[areaId], self.array
	local count = 0
	local minIX, maxIX, minIY, maxIY = area.maxIX, area.minIX, area.maxIY, area.minIY
	for _, _, ix, iy in array:by_isquare(minIX, minIY, maxIX, maxIY) do
		local cell = array:get_ixiy(ix, iy)
		if cell and cell.aid == areaId then
			if iy < minIY then minIY = iy end
			if iy > maxIY then maxIY = iy end
			if ix < minIX then minIX = ix end
			if ix > maxIX then maxIX = ix end
			count = count + 1
		end
	end
	area.minIX, area.maxIX, area.minIY, area.maxIY = minIX, maxIX, minIY, maxIY
	area.count = count
end
-- ok
local function update_reduced_area(self, areaId)
	local area = self.areas[areaId]
	if not area then return end
	if area.count <= 0 then
		return area_delete(self, areaId)
	end
	update_area_size(self, areaId)
end
 -- ok
local function player_in_rect_one_more(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		local cell = array:get_ixiy(ix, iy) -- хотя бы 1 свой кусочек
		if is_cell_player(cell, playerId) then return true end
	end
	return false
end
 -- ok
local function empty_in_rect_one_more(self, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		local cell = array:get_ixiy(ix, iy)
		if not cell then return true end
	end
	return false
end
-- ok
local function rect_build(self, playerId, areaId, ix0, iy0, ix1, iy1)
	local array = self.array
	local area = self.areas[areaId]
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		if not array:get_ixiy(ix, iy) then
			local cell = cell_create(playerId, areaId)
			array:set_ixiy(ix, iy, cell)
			area_add_cell(area, cell, ix, iy)
		end
	end
end
-- ok
local function rect_transfer(self, sellerId, buyerId, areaId, ix0, iy0, ix1, iy1)
	local array, touched_areas = self.array, {}
	local area = self.areas[areaId]
	for _, _, ix, iy in array:by_isquare(ix0,iy0,ix1,iy1) do
		local cell = array:get_ixiy(ix, iy)
		if cell then
			if cell.pid == sellerId then
				local aid = cell.aid
				area.count = area.count - 1
				touched_areas[aid] = true
				cell.pid, cell.aid = buyerId, areaId
				area_add_cell(area, cell, ix, iy)
			end
		else
			cell = cell_create(buyerId, areaId)
			array:set_ixiy(ix, iy, cell)
			area_add_cell(area, cell, ix, iy)
		end
	end
	for aid, _ in pairs(touched_areas) do
		update_reduced_area(self, aid)
	end
end
-- ok
local function near_area_to_rect(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0-1, iy0, ix0-1, iy1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy1+1, ix1, iy1+1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	for _, _, ix, iy in array:by_isquare(ix1+1, iy0, ix1+1, iy1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy0-1, ix1, iy0-1) or table_empty do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then return cell.aid end
	end
	return nil
end
-- ok
local function is_rect_surrounded_player(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	for _, _, ix, iy in array:by_isquare(ix0-1, iy0, ix0-1, iy1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy1+1, ix1, iy1+1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	for _, _, ix, iy in array:by_isquare(ix1+1, iy0, ix1+1, iy1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy0-1, ix1, iy0-1) or table_empty do
		if not is_cell_player(array:get_ixiy(ix, iy), playerId) then return false end
	end
	return true
end
-- ok
local function max_area_player_rect(self, playerId, ix0, iy0, ix1, iy1)
	local array = self.array
	local max, tbl, areaId = 0, {}
	for _, _, ix, iy in array:by_isquare(ix0, iy0, ix1, iy1) do
		local cell = array:get_ixiy(ix, iy)
		if is_cell_player(cell, playerId) then
			local aid = cell.aid
			tbl[aid] = tbl[aid] and tbl[aid]+1 or 1
		end
	end
	for aid, num in pairs(tbl) do
		if max < num then max, areaId = num, aid end
	end
	return areaId
end

local function mark_border(self, pid, ix0, iy0, ix1, iy1)
	local array = self.array
	local start, first = nil, true -- first cell
	local count, mark, rect  = 0, {}, {}
	-- print('mark_border', ix0, iy0, ix1, iy1)
	for _, _, ix, iy in array:by_isquare(ix0-1, iy0, ix0-1, iy1) or table_empty do
		local cell = array:get_ixiy(ix0, iy) -- left
		mark[iy] = {}
		rect[iy] = {}
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) then
			mark[iy][ix0] = true
			count = count + 1
			if first then start = {2, ix0, iy} first = nil end
		end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy1+1, ix1, iy1+1) or table_empty do
		local cell = array:get_ixiy(ix, iy1) -- top
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) and not mark[iy1][ix] then
			mark[iy1][ix] = true
			count = count + 1
			if first then start = {3, ix, iy1} first = nil end
		end
	end
	for _, _, ix, iy in array:by_isquare(ix1+1, iy0, ix1+1, iy1) or table_empty do
		local cell = array:get_ixiy(ix1, iy) -- right
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) and not mark[iy][ix1] then
			mark[iy][ix1] = true
			count = count + 1
			if first then start = {4, ix1, iy} first = nil end
		end
	end
	for _, _, ix, iy in array:by_isquare(ix0, iy0-1, ix1, iy0-1) or table_empty do
		local cell = array:get_ixiy(ix, iy0) -- bot
		if (not cell or is_cell_player(cell, pid))
			and not is_cell_player(array:get_ixiy(ix, iy), pid) and not mark[iy0][ix] then
			mark[iy0][ix] = true
			count = count + 1
			if first then start = {1, ix, iy0} first = nil end
		end
	end
	-- запонение прямоугольника новой области
	for _, _, ix, iy in array:by_isquare(ix0, iy0, ix1, iy1) do
		local cell = array:get_ixiy(ix, iy)
		if not cell then rect[iy][ix] = true end
	end
	-- local rect = {}
	-- for iy=iy0, iy1 do
	-- 	local trect = {}
	-- 	rect[iy] = trect
	-- 	for ix=ix0, ix1 do
	-- 		trect[ix] = true
	-- 		-- print('rect', ix, iy)
	-- 	end
	-- end
	if start then -- значит забагаюзали, убирается дырка
		return rect, mark, count, start[1], start[2], start[3]
	else
		return rect, mark, count
	end
end

local function stbl(tbl, x, y) return tbl[y] and tbl[y][x] end
local function bypass_border(self, pid, ix0, iy0, ix1, iy1)
	local array = self.array
	local rect, mark, count, state, x0, y0 = mark_border(self, pid, ix0, iy0, ix1, iy1)
	if not state then return true end
	local tx, ty = x0, y0
	local opt = 0
	-- print('x0, y0',x0, y0)
	repeat
		if stbl(mark, tx, ty) then
			mark[ty][tx] = nil
			count = count - 1
		end
		while true do
			opt = opt + 1
			if opt > 400 then print('bypass_border error opt') return end
			-- print('tx '..tx, 'tx '..ty, 'state '..state)
			if state == 1 then
				local cell = array:get_ixiy(tx-1, ty-1)
				if is_cell_player(cell, pid) or stbl(rect, tx-1, ty-1) then
					tx, ty, state = tx-1, ty-1, 4 break
				else
					cell = array:get_ixiy(tx-1, ty)
					if is_cell_player(cell, pid) or stbl(rect, tx-1, ty) then
						tx = tx-1 break
					else
						state = 2
					end
				end
			elseif state == 2 then
				local cell = array:get_ixiy(tx-1, ty+1)
				if is_cell_player(cell, pid) or stbl(rect, tx-1, ty+1) then
					tx, ty, state = tx-1, ty+1, 1 break
				else
					cell = array:get_ixiy(tx, ty+1)
					if is_cell_player(cell, pid) or stbl(rect, tx, ty+1) then
						ty = ty+1 break
					else
						state = 3
					end
				end
			elseif state == 3 then
				local cell = array:get_ixiy(tx+1, ty+1)
				if is_cell_player(cell, pid) or stbl(rect, tx+1, ty+1) then
					tx, ty, state = tx+1, ty+1, 2 break
				else
					cell = array:get_ixiy(tx+1, ty)
					if is_cell_player(cell, pid) or stbl(rect, tx+1, ty) then
						tx = tx+1 break
					else
						state = 4
					end
				end
			elseif state == 4 then
				local cell = array:get_ixiy(tx+1, ty-1)
				if is_cell_player(cell, pid) or stbl(rect, tx+1, ty-1) then
					tx, ty, state = tx+1, ty-1, 3 break
				else
					cell = array:get_ixiy(tx, ty-1)
					if is_cell_player(cell, pid) or stbl(rect, tx, ty-1) then
						ty = ty-1 break
					else
						state = 1
					end
				end
			end
		end
	until tx==x0 and ty==y0
	-- print('count', count)
	return count <= 0
end

-- basic
local function can_build(self, playerId, x0, y0, x1, y1)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x0,y0,x1,y1)
	if not is_player(self, playerId)
		or not minIX
		or not empty_in_rect_one_more(self, minIX, minIY, maxIX, maxIY) then
		return false
	end
	local areaId = max_area_player_rect(self, playerId, minIX, minIY, maxIX, maxIY)
				or near_area_to_rect(self, playerId, minIX, minIY, maxIX, maxIY)
	-- print('can_build areaId', areaId)
	if not areaId then return false end
	if not bypass_border(self, playerId, minIX, minIY, maxIX, maxIY) then
		-- print('bypass_border error 2')
		return false
	end
	return true, areaId
end


-- basic
local function build_land(self, playerId, x0, y0, x1, y1)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x0,y0,x1,y1)
	if not (minIX and is_player(self, playerId)) then return false end
	local can, areaId = self:can_build(playerId, x0, y0, x1, y1)
	if not can then return false end
	rect_build(self, playerId, areaId, minIX, minIY, maxIX, maxIY)
	return true
end


-- basic
local function sell_land(self, sellerId, buyerId, x0, y0, x1, y1)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x0,y0,x1,y1)
	if not (minIX and is_player(self, sellerId) and is_player(self, buyerId))
		or is_rect_surrounded_player(self, sellerId, minIX, minIY, maxIX, maxIY)
		or not player_in_rect_one_more(self, sellerId, minIX, minIY, maxIX, maxIY) then
		return false
	end
	local areaId = near_area_to_rect(self, buyerId, minIX, minIY, maxIX, maxIY)
	if not areaId then
		areaId = area_create(self, {
			pid = buyerId,
			x = (x0+x1)*0.5,
			y = (y0+y1)*0.5,
			radius = 0,
			count = 0,
			minIX = minIX,
			maxIX = maxIX,
			minIY = minIY,
			maxIY = maxIY,
		}).aid
	end
	rect_transfer(self, sellerId, buyerId, areaId, minIX, minIY, maxIX, maxIY)
	return true
end

local function can_new_village(self, playerId, x0, y0, radius)
	-- ищем перекрывающиеся деревни
	for aId, tbl in pairs(self.areas) do
		local dist = (tbl.x-x0)*(tbl.x-x0)+(tbl.y-y0)*(tbl.y-y0)
		local rds = radius+tbl.radius
		if dist < rds*rds then
			-- print('dist error')
			return false
		end
	end
	-- смотрим не заняты ли клетки
	local array = self.array
	for _, _, ix, iy in array:by_circle(x0, y0, radius) do
		local cell = array:get_ixiy(ix, iy)
		if cell and cell.pid~=playerId then
			-- print('array error')
			return false
		end
	end
	return true
end

-- basic
local function new_village(self, playerId, x, y, rds)
	if not (playerId and x and y and rds)
		or not can_new_village(self, playerId, x, y, rds) then
		return false
	end
	player_create(self, playerId)
	local minIX, minIY, maxIX, maxIY = self.array:rect_to_indexes(x-rds,y-rds,x+rds,y+rds)
	local area = area_create(self, {
		x = x,
		y = y,
		radius = rds,
		pid = playerId,
		count = 0,
		minIX = minIX,
		maxIX = maxIX,
		minIY = minIY,
		maxIY = maxIY,
	})
	local array, areaId = self.array, area.aid
	for _, _, ix, iy in array:by_circle(x, y, rds) do
		-- print('v',ix,iy)
		local cell = cell_create(playerId, areaId)
		array:set_ixiy(ix, iy, cell)
		area_add_cell(area, cell, ix, iy)
	end
	if area.count <= 0 then
		print('error new_village')
		area_delete(self, area) --
		return false
	end
	-- print('areaId', areaId)
	return true
end

local function area_points(self, areaId)
	local array, a = self.array, self.areas[areaId]
	if not a then return nil end
	local couples = {}
	for x, y, ix, iy in array:by_isquare(a.minIX, a.minIY, a.maxIX, a.maxIY) do
		local cell = array:get_ixiy(ix, iy)
		if cell and cell.aid == areaId then
			table.insert(couples, x)
			table.insert(couples, y)
		end
	end
	return couples
end

local function for_sort(a,b) return a.id < b.id end
local function player_points(self, playerId)
	if not is_player(self, playerId) then return nil end
	local areas = self.players[playerId].areas
	local list = {}
	for areaId, area in pairs(areas) do -- desync
		table.insert(list, {id=area.aid, data = area_points(self, areaId)})
	end
	table.sort(list, for_sort)
	return list
end

local index = {
	can_build = can_build,
	build_land = build_land,
	sell_land = sell_land,
	new_village = new_village,
	player_points = player_points,
}

local mt = {__index = index}

function Territory(minX, minY, maxX, maxY, step)
	local array = SquareArray(minX, minY, maxX, maxY, step)
	local object = {
		step = step, offsetY = -minY, offsetX = -minX,
		array = array,
		areas = {}, players = {},
		gen = area_generator()
	}
	return setmetatable(object, mt)
end
end

----------------------------------------------- Пример использования Territory
do
local world_rect = GetWorldBounds()
-- получаем все значение карты
local minX, maxX, minY, maxY = GetRectMinX(world_rect), GetRectMaxX(world_rect), GetRectMinY(world_rect), GetRectMaxY(world_rect)
local step = 64 -- шаг сетки
local map = Territory(minX, minY, maxX, maxY, step)
local home_radius = 150 -- радиус деревни
local buy_radius = step/2 -- радиус покупки земли


-------------------------- пример - отображение в игре
local player_units = {[1]={},[2]={}} -- храним юнитов отображения для plyerId-1 и plyerId-2

-- обновляем ландшафт в точке (пример)
local function update_land_ex(pId, x, y)
	local red = pId==1 and 255 or 150
	local blue = pId==2 and 255 or 150
	local green = 150
	local alpha = 100
	local size = 64 -- размер юнита
	local scale = step/size
	-- 1747988528 это его UID
	local uni = CreateUnit(Player(pId), 1747988528, x, y, 0)
	SetUnitScale(uni, scale, scale, scale)
	SetUnitVertexColor(uni, red, blue, green, alpha)
	return uni
end

-- обновляем отображения для playerId
local function update_land(playerId)
	local tbl = {}
	local pl_areas = map:player_points(playerId)
	for _, area in ipairs(pl_areas) do -- идем по всем областям (area) игрока (playerId)
		area = area.data
		for i=1, #area, 2 do
			-- area[i], area[i+1] это x и y
			tbl[65536*area[i+1]+area[i]] = {area[i], area[i+1]}
		end
	end
	local dict = player_units[playerId]
	--// десинх
	for k, uni in pairs(dict) do if not tbl[k] then RemoveUnit(uni) dict[k] = nil end end -- тут удаляю юнитов которые больше не нужно отображать (квадратики)
	for k, couple in pairs(tbl) do if not dict[k] then dict[k] = update_land_ex(playerId, couple[1], couple[2]) end end -- тут дорисовываю новых юнитов
	--//
end
--------------------------

-- Триггер который регистрирует нажатия на способности
-- 1 и 2 это тестовые plyerId
local trig = CreateTrigger()
TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
TriggerAddAction( trig, function()
	local aid, tx, ty = GetSpellAbilityId(), GetSpellTargetX(), GetSpellTargetY()
	local basic = 1093677104 -- это просто id способности
	local result
	print('tx,ty',tx,ty)
	if aid==basic then -- create 1
		print('create 1')
		result = map:new_village(1, tx, ty, home_radius, step)
		if result then update_land(1) end
		print(result)
	elseif aid==basic+1 then -- create 2
		print('create 2')
		result = map:new_village(2, tx, ty, home_radius, step)
		if result then update_land(2) end
		print(result)
	elseif aid==basic+2 then -- buy 1
		print('buy 1')
		result = map:build_land(1, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(1) end
		print(result)
	elseif aid==basic+3 then -- buy 2
		print('buy 2')
		result = map:build_land(2, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(2) end
		print(result)
	elseif aid==basic+4 then -- send 1 -> 2
		print('send 1 -> 2')
		result = map:sell_land(1,2, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(1) update_land(2) end
		print(result)
	elseif aid==basic+5 then -- send 2 -> 1
		print('send 2 -> 1')
		result = map:sell_land(2,1, tx-buy_radius,ty-buy_radius,tx+buy_radius,ty+buy_radius)
		if result then update_land(1) update_land(2) end
		print(result)
	end
end)

end
-----------------------------------------------

	end
end
2
17
2 года назад
2
Добавил версию для reforged в архив
Чтобы оставить комментарий, пожалуйста, войдите на сайт.