27

» WarCraft 3 / Происхождение (Конкурс ландшафта 2020)

классное отражение от луны

я еще подумал там инопланетяне. светящие елки) эффект кольца вверх, как будто нло прилетело и елки вверх, и вы на корабле: " здравствуй скорпик, мы инопланетяне".

по реке почему не могу ходить.

кто такие красивые мосты в лесу строит

ландшафт для леса неплох ну для hd графона.

освещение и отражение классное
27

» WarCraft 3 / Создание пассивки

skin я так понимаю, это текстура модели. Так вот, есть такие функции. Однако, там указывают не сразу строки, а ид (чего конкретно не понять, надо найти пример)
Под скином, насколько я помню, подразумевается айди юниита, чью кожу будет надевать наш герой.
Ну тут можно в текстовом файле создать свой id попробовать. Наверн сработает? Если нестандартного юнита создать, он тоже его кожу берет? мне кажется надо прописывать в текст unitskin.txt и импортировать в карту по пути units/unitskin.txt
как найти женских персов и разновидности в рефе? я видел разных паладосов только в ютубе
27

» WarCraft 3 / Создание пассивки

skin я так понимаю, это текстура модели. Так вот, есть такие функции. Однако, там указывают не сразу строки, а ид (чего конкретно не понять, надо найти пример)
вот список со Skin
function BlzCreateDeadDestructableWithSkin takes integer objectid, real x, real y, real face, real scale, integer variation, integer skinId returns destructable
function BlzCreateDeadDestructableZWithSkin takes integer objectid, real x, real y, real z, real face, real scale, integer variation, integer skinId returns destructable
function BlzCreateDestructableWithSkin takes integer objectid, real x, real y, real face, real scale, integer variation, integer skinId returns destructable
function BlzCreateDestructableZWithSkin takes integer objectid, real x, real y, real z, real face, real scale, integer variation, integer skinId returns destructable
function BlzCreateItemWithSkin takes integer itemid, real x, real y, integer skinId returns item
function BlzCreateUnitWithSkin takes player id, integer unitid, real x, real y, real face, integer skinId returns unit
function BlzGetItemSkin takes item whichItem returns integer
function BlzGetUnitSkin takes unit whichUnit returns integer
function BlzSetItemSkin takes item whichItem, integer skinId returns nothing
function BlzSetUnitSkin takes unit whichUnit, integer skinId returns nothing

самому интересно. Однако, сейчас я не могу проверить, тк занят другим.
Но стоит учесть, что в рефе такие могут и не заработать. тот кто добавлял эти функции явно через жопу делал. запихал многие не рабочие функции

в редакторе рефа ид скинов означают ид юнита. Что ли ид юнита берем?
Посмотрел в архиве рефа:
есть файлы по пути units/
Видимо, подразумевается прописывать в текстовые файлы. А то толк не пойму в этих ид. Ну извраты. Хотелось бы проще изменять скиф заданной тексторой

У рефа наверн не получится менять текстуры. Под Skin подразумевается целый набор данных. Там меняется модель, размеры, и другие графические данные. Тем более наследует параметры из итемов, пример морфы. Вам придется указать собственную модель и данные. по сути это просто морф
hive (как использовать)
hive (пояснения, плюс как женские скины использовать)
Загруженные файлы
27

» WarCraft 3 / Не найти фреймы в 1.26а

framedef.toc
UI\FrameDef\GlobalStrings.fdf
UI\FrameDef\InfoPanelStrings.fdf
UI\FrameDef\NetworkStrings.fdf
UI\FrameDef\DateStrings.fdf
UI\FrameDef\Glue\DialogWar3.fdf
UI\FrameDef\Glue\MapInfoPane.fdf
UI\FrameDef\Glue\AdvancedOptionsDisplay.fdf
UI\FrameDef\Glue\AdvancedOptionsPane.fdf
UI\FrameDef\Glue\ListBoxWar3.fdf
UI\FrameDef\Glue\MapListBox.fdf
UI\FrameDef\Glue\DecoratedMapListBox.fdf
UI\FrameDef\Glue\MapPreferenceBox.fdf
UI\FrameDef\Glue\CheckListBox.fdf
UI\FrameDef\Glue\MainMenu.fdf
UI\FrameDef\Glue\MovieScreen.fdf
UI\FrameDef\Glue\SplashScreen.fdf
UI\FrameDef\Glue\Loading.fdf
UI\FrameDef\Glue\LoadSavedGameScreen.fdf
UI\FrameDef\Glue\OptionsMenu.fdf
UI\FrameDef\Glue\SinglePlayerMenu.fdf
UI\FrameDef\Glue\ViewReplayScreen.fdf
UI\FrameDef\Glue\CustomCampaignMenu.fdf
UI\FrameDef\Glue\CampaignMenu.fdf
UI\FrameDef\Glue\TeamSetup.fdf
UI\FrameDef\Glue\PlayerSlot.fdf
UI\FrameDef\Glue\Skirmish.fdf
UI\FrameDef\Glue\ScoreScreen.fdf
UI\FrameDef\Glue\LocalMultiplayerJoin.fdf
UI\FrameDef\Glue\LocalMultiplayerCreate.fdf
UI\FrameDef\Glue\LocalMultiplayerLoad.fdf
UI\FrameDef\Glue\GameChatroom.fdf
UI\FrameDef\Glue\CampaignListBox.fdf
UI\FrameDef\Glue\BattleNetMain.fdf
UI\FrameDef\Glue\BattleNetMatchmakerPanel.fdf
UI\FrameDef\Glue\BattleNetNewsBox.fdf
UI\FrameDef\Glue\BattleNetUserListBox.fdf
UI\FrameDef\Glue\BattleNetChatPanel.fdf
UI\FrameDef\Glue\BattleNetChatActionMenu.fdf
UI\FrameDef\Glue\BattleNetStandardPanel.fdf
UI\FrameDef\Glue\BattleNetTeamPanel.fdf
UI\FrameDef\Glue\BattleNetTeamInvitation.fdf
UI\FrameDef\Glue\BattleNetProfilePanel.fdf
UI\FrameDef\Glue\BattleNetCustomJoinPanel.fdf
UI\FrameDef\Glue\BattleNetCustomCreatePanel.fdf
UI\FrameDef\Glue\BattleNetCustomLoadPanel.fdf
UI\FrameDef\Glue\BattleNetScheduledGame.fdf
UI\FrameDef\Glue\BattleNetChatroom.fdf
UI\FrameDef\Glue\BattleNetFriendsListBox.fdf
UI\FrameDef\Glue\BattleNetFriendsPane.fdf
UI\FrameDef\Glue\BattleNetClanMateListBox.fdf
UI\FrameDef\Glue\BattleNetClanPane.fdf
UI\FrameDef\Glue\BattleNetClanInvitation.fdf
UI\FrameDef\Glue\BattleNetStatusBox.fdf
UI\FrameDef\Glue\BattleNetIconSelectBox.fdf
UI\FrameDef\UI\ConsoleUI.fdf
UI\FrameDef\UI\CommandBar.fdf
UI\FrameDef\UI\AllianceSlot.fdf
UI\FrameDef\UI\AllianceDialog.fdf
UI\FrameDef\UI\ChatDialog.fdf
UI\FrameDef\UI\LogDialog.fdf
UI\FrameDef\UI\EscMenuMainPanel.fdf
UI\FrameDef\UI\EscMenuOptionsPanel.fdf
UI\FrameDef\UI\EscMenuSaveGamePanel.fdf
UI\FrameDef\UI\GameResultDialog.fdf
UI\FrameDef\UI\UpperButtonBar.fdf
UI\FrameDef\UI\InventoryBar.fdf
UI\FrameDef\UI\ResourceBar.fdf
UI\FrameDef\UI\SimpleInfoPanel.fdf
UI\FrameDef\UI\SuspendDialog.fdf
UI\FrameDef\UI\UnresponsiveDialog.fdf
UI\FrameDef\UI\ReplayPanel.fdf
UI\FrameDef\UI\ObserverPanel.fdf
UI\FrameDef\UI\QuestDialog.fdf
UI\FrameDef\UI\TimerDialog.fdf
UI\FrameDef\UI\MiniMapFrame.fdf
UI\FrameDef\UI\Leaderboard.fdf
UI\FrameDef\UI\Multiboard.fdf
UI\FrameDef\UI\CinematicPanel.fdf
UI\FrameDef\UI\ScriptDialog.fdf
UI\FrameDef\UI\GameSaveSplashDialog.fdf
UI\FrameDef\UI\BrowserFrame.fdf
27

» WarCraft 3 / Не найти фреймы в 1.26а

в рефе тоже неизвестно что будет, если заменить дефолт fdf будет ли работать (такое не практивал). Но все параметры размещения относительного меин фрейма указаны
command bar первый
Frame "SIMPLEBUTTON" "CommandButtonTemplate" {
	Width 0.039,
	Height 0.039,	
}

Frame "SIMPLEFRAME" "CommandBarFrame" {
    UseActiveContext,
    SetAllPoints,
	Width 0.1745,
	Height 0.129,
		
	Frame "SIMPLEBUTTON" "CommandButton_0" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_1" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0434, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_2" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0868, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_3" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1302, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_4" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0000, -0.044,
	}		
		
	Frame "SIMPLEBUTTON" "CommandButton_5" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0434, -0.044,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_6" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0868, -0.044,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_7" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1302, -0.044,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_8" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0000, -0.0880,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_9" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0434, -0.0880,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_10" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0868, -0.0880,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_11" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1302, -0.0880,
	}			
}

Frame "SIMPLEFRAME" "ReplayCommandBarFrame" {
    UseActiveContext,
    SetAllPoints,
	Width 0.0,
	Height 0.0
}
полный код CommandBar
Texture "CommandBarTemplate" {
	Width 0.182,
	Height 0.091,
	TexCoord 0, 1, 0, 1,
	AlphaMode "ALPHAKEY",
	Anchor TOPLEFT, 0.0, 0.0,
}

Frame "SIMPLEBUTTON" "CommandButtonTemplate" {
	Width 0.0265,
	Height 0.0265,	
}

Frame "SIMPLEBUTTON" "CommandButtonTemplate_Row5" {
	Width 0.032,
	Height 0.032,	
}

Frame "SIMPLEBUTTON" "CommandButtonTemplate_Row7" {
	Width 0.0225,
	Height 0.0225,	
}

Frame "SIMPLEFRAME" "CommandBarFrame" {
    UseActiveContext,
    SetAllPoints,
	Width 0.24,
	Height 0.105,
	
	Frame "SIMPLEBUTTON" "CommandButton_0" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_1" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0279, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_2" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0559, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_3" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0839, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_4" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1119, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_5" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1399, 0.0,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_6" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1679, 0.0,
	}		
		
	Frame "SIMPLEBUTTON" "CommandButton_7" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0, -0.0285,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_8" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0279, -0.0285,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_9" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0559, -0.0285,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_10" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0839, -0.0285,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_11" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1119, -0.0285,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_12" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1399, -0.0285,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_13" INHERITS "CommandButtonTemplate" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1679, -0.0285,
	}	
	
	
	Frame "SIMPLEBUTTON" "CommandButton_0a" INHERITS "CommandButtonTemplate_Row5" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0, 0.001,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_1a" INHERITS "CommandButtonTemplate_Row5" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0337, 0.001,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_2a" INHERITS "CommandButtonTemplate_Row5" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0674, 0.001,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_3a" INHERITS "CommandButtonTemplate_Row5" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1010, 0.001,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_4a" INHERITS "CommandButtonTemplate_Row5" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1348, 0.001,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_5a" INHERITS "CommandButtonTemplate_Row5" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1685, 0.001,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_6a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.2021, 0.001,
	}		
		
	Frame "SIMPLEBUTTON" "CommandButton_7a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0, -0.033,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_8a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0242, -0.033,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_9a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0484, -0.033,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_10a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0725, -0.033,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_11a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.0968, -0.033,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_12a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1210, -0.033,
	}
	
	Frame "SIMPLEBUTTON" "CommandButton_13a" INHERITS "CommandButtonTemplate_Row7" {
		SetPoint TOPLEFT, "CommandBarFrame", TOPLEFT, 0.1451, -0.033,
	}	
}

Frame "SIMPLEFRAME" "SimpleCommandBarGrid" {
    UseActiveContext,
    SetAllPoints,
	
    Texture "Grid_5_7" INHERITS "CommandBarTemplate"{
        File "UI/Console/Reforged/MinHudCommandGrid_5_7.blp",
    }
	
    Texture "Grid_6_6" INHERITS "CommandBarTemplate"{
        File "UI/Console/Reforged/MinHudCommandGrid_6_6.blp",
    }
}

проще свой интерфейс забабахать
27

» WarCraft 3 / Не найти фреймы в 1.26а

Там есть такое в "графика" Написано расположение (Y) и (X)
Он спрашивает где найти данные о расположении фреймов, размеры, привязки. Все лежит в ui. Но не может быть, что в архивах ничего нет. даже в старом 1.26 находил все. Но это будет не точно.

для рефа. Сейчас не за компом

Это вы должны открывать блокнотом файлы fdf. Там лежат данные о расположении. Найти что то похожее
27

» WarCraft 3 / Уменьшение здания?

тип текстуры пути здании и деревьев, и блокираторов тоже влияет на проходимость
На зданиях лучше указать "Движение - Тип движения" - "Нет", иначе другие типы движения блокируют проходы (если присмотреться у большинства зданий не указан никакой тип движения, кроме эльфийских древ). У эльфийских древ есть способность "Пустить корни", которая делает неподвижными.
Зданиям и декорациям задают еще "Пути - Карта путей" - например текстура "Портал" или отсутствие текстуры ("Нет") делают здание проходимым. Тогда юниты через это свободно перемещаются. Какая-либо другая текстура может блокировать проход. Точно также происходит с декорациями, разрушаемыми декорациями (блокираторами). Об этом можно прочитать здесь
27

» WarCraft 3 / Не найти фреймы в 1.26а

ui/framedef/ui/commandbar.fdf - станд сетка 12
ui/framedef/ui/reforged/commandbar.fdf - какая-то другая сетка, где кнопок 14
27

» WarCraft 3 / Контроль земли

у меня есть полный код с картой 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
27

» WarCraft 3 / Проблемы с begins casting an ability и finishes casting an abily


триггерно все делать, если ставить такой вопрос. Мы уже не сможешь механику вара изменить, если приказом "стоп" перебивается заклинание, значит, start effect of ability не сработал. Именно, в этот момент происходит забор маны
Все картоделы не могут ответить, но кто сталкивался, может ответить. Это надо тестить.

если у вас даймик, то нужно убрать у него анимации , тогда у даймика не будет задержек и заклинания сработают сразу, без задержек
графика - Анимация: Точка броска и Графика - Анимация: Обратный ход броска
Art - Animation - Cast Point (Real)
Art - Animation - Cast Backswing (Real)
ссылка

А в заклинании убрать CastTime (задержка)
27

» WarCraft 3 / Отловить нажатие ЛКМ по области

МрачныйВорон, он на 1.27 и гуи просит если что))
ну я невнимательный. он просит отключить/включать круг выделения и подсказки над головой. Тогда есть BlzGetMouseFocusUnit. Таймером можно проверять, что за юнит у игрока выделен благодаря константе BlzGetMouseFocusUnit. Выделен можно только один какой то. Все профит. Но однако, как будут работать функции сокрытия. События мыши есть в 1.27?
27

» WarCraft 3 / Отловить нажатие ЛКМ по области

  1. в рефе можно скрыть прицел BlzEnableCursor еще пример. Однако, за пример не отвечаю. Надо его проверить на десинх
  2. скрыть можно каналом пример
  3. есть события наведения мыши, они выводят мировые координаты. Сравниваешь, где лежит точка. Лежит ли в ректе. Все профит.
  4. для таких случаев рекомендуется сначала фрейм-маску на весь экран сделать, так клик пройдет не по точке на экране, а по фрейму. И прицел тогда не закроется. Но координаты все равно можно получить, они даже через интерфейс меняются. Иначе, не сделав этого, прицел будет закрываться при нажатии. А так нажал = а тут сообщение: вы не можете здесь кликать
  5. ловим каст => значит успешна

надо будет потом пример сделать

можно ли как то скрыть круг выбора с этого юнита
можно, но это будет визуально на всех работать.Есть команды отключения круга, но там кажись на всех работает.
код
это новые рефорджовские. Есть еще и старые помню в 1.26, поищи сам. Но вот работают илит новые, не тестировалось
---@param enableSelection boolean
---@param enableSelectionCircle boolean
---@return nothing
function BlzEnableSelections(enableSelection, enableSelectionCircle) end	-- (native)
---@return boolean
function BlzIsSelectionEnabled() end	-- (native)
---@return boolean
function BlzIsSelectionCircleEnabled() end	-- (native)
Можно ловить наведение на BlzGetMouseFocusUnit, если на никакого юнита не навел игрок, значит, переменная пуста.

название над ним
это фреймы пример вот здесь (1. info подсказки и bars). Они тоже отключают локально у игрока все. Но это можно исправить BlzGetMouseFocusUnit

для таких случаев рекомендуется сначала фрейм-маску на весь экран сделать
также помогает фрейм не выделять объекты на карте. И поэтому не увидите подсказки, и что юнит BlzGetMouseFocusUnit выделен. Это больше подходит для заклинании с точкой
27

» WarCraft 3 / Как убрать иконки передвижения у конкретно взятого юнита?

В рефордже перестают адекватно работать сокрытие через импорт, или ро работает 0,-11 (но не всегда). Я уже не помню, что там. Помню, что я другое решение нашел - через нативку.
Можно спрятать этими нативками в рефе. Однако, они тоже не всегда адекватно работают. Можно спрятать на совсем, и обратно не хотят отображаться. Мб дело в моих кривых руках. чисто спрятать и показать => работает. Но говорят, что она багнутая.
native BlzUnitHideAbility takes unit whichUnit, integer abilId, boolean flag returns nothing
или это. выключать их мб и не стоит, но вот второй параметр отвечает за сокрытие. Но работает и багуется также.
function BlzUnitDisableAbility takes unit whichUnit, integer abilId, boolean flag, boolean hideUI returns nothing
native BlzUnitDisableAbilitytakes unit whichUnit, integer abilId, boolean flag, boolean hideUI returns nothing

xgm.guru/p/blog-steal-nerves/ability3 => раздел интерфейс команды приказа
27

» WarCraft 3 / Проблема с магазином

Возможно можно 12 слотов. я уже не помню наработку 200 лет писал. И забыл как с итемами взаимодействовать.
27

» WarCraft 3 / Проблема с магазином

Описана похажая проблема поищи там

В комментах мой комментарии про возможные баги.

Не знаю когда у вас наполняется и как. Но судя добавлением. Да есть там такой баг, когда итем просто пропадает и все. Такое было. Примерно при повторном добавлении только встречал. Причинах в этих пополняющих стэках магаза. Факов еще помогал мне
27

» WarCraft 3 / По какому принципу реализована кастомная система статов? Reforge

Как превышают лимит скорости атаки в 400%?
там спец нативкой время cooldawn, или по вашему BAT можно уменьшать. Она не зависит от процкентов. Проценты придумали для этих.

лимит 400% можно превысить всякими баффами абилок на скорость. но за точность инфы не ручаюсь. Надо самому протестить. Возможно 400% это предел.
Как снижают длительность бафов/дебафов?
можно через фреймы. или через таймером, кастомизацию баффов тоже можно было делать и в 1.26 без мемов. Еще помню, раньше заменяли способности. Возможна можно задать длительность абилкам через филды. Но напомню, что нельзя всем изменять.
Как увеличивают вероятность срабатывания способностей?
смотря какие способности. чаще через триггеры через рандом.
27

» WarCraft 3 / Дальнобойные атаки сквозь препятствия

изменить тип атаки? тогда не будут стрелять через ограды. Когда обойдет ограду, то снимай/выдай классификацию, и вау-ля можно снова атаковать.
или нужен блок? можно сделать триггерно движение снарядов, когда снаряд врезается об щит ментально, делаем отскок, удаляем. если это атака, то проще сделать триггерно атаку, у одного мастера можно узнать. xgm.guru/user/rsfghd если если эта какая то способность, то мб проще сделать триггерно абилку снарядов.