я еще подумал там инопланетяне. светящие елки) эффект кольца вверх, как будто нло прилетело и елки вверх, и вы на корабле: " здравствуй скорпик, мы инопланетяне".
skin я так понимаю, это текстура модели. Так вот, есть такие функции. Однако, там указывают не сразу строки, а ид (чего конкретно не понять, надо найти пример)
Под скином, насколько я помню, подразумевается айди юниита, чью кожу будет надевать наш герой.
Ну тут можно в текстовом файле создать свой id попробовать. Наверн сработает? Если нестандартного юнита создать, он тоже его кожу берет? мне кажется надо прописывать в текст unitskin.txt и импортировать в карту по пути units/unitskin.txt
как найти женских персов и разновидности в рефе? я видел разных паладосов только в ютубе
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 (пояснения, плюс как женские скины использовать)
в рефе тоже неизвестно что будет, если заменить дефолт fdf будет ли работать (такое не практивал). Но все параметры размещения относительного меин фрейма указаны
Там есть такое в "графика" Написано расположение (Y) и (X)
Он спрашивает где найти данные о расположении фреймов, размеры, привязки. Все лежит в ui. Но не может быть, что в архивах ничего нет. даже в старом 1.26 находил все. Но это будет не точно.
тип текстуры пути здании и деревьев, и блокираторов тоже влияет на проходимость
На зданиях лучше указать "Движение - Тип движения" - "Нет", иначе другие типы движения блокируют проходы (если присмотреться у большинства зданий не указан никакой тип движения, кроме эльфийских древ). У эльфийских древ есть способность "Пустить корни", которая делает неподвижными.
Зданиям и декорациям задают еще "Пути - Карта путей" - например текстура "Портал" или отсутствие текстуры ("Нет") делают здание проходимым. Тогда юниты через это свободно перемещаются. Какая-либо другая текстура может блокировать проход. Точно также происходит с декорациями, разрушаемыми декорациями (блокираторами). Об этом можно прочитать здесь
у меня есть полный код с картой 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
триггерно все делать, если ставить такой вопрос. Мы уже не сможешь механику вара изменить, если приказом "стоп" перебивается заклинание, значит, start effect of ability не сработал. Именно, в этот момент происходит забор маны
Все картоделы не могут ответить, но кто сталкивался, может ответить. Это надо тестить.
если у вас даймик, то нужно убрать у него анимации , тогда у даймика не будет задержек и заклинания сработают сразу, без задержек
графика - Анимация: Точка броска и Графика - Анимация: Обратный ход броска
Art - Animation - Cast Point (Real)
Art - Animation - Cast Backswing (Real) ссылка
ну я невнимательный. он просит отключить/включать круг выделения и подсказки над головой. Тогда есть BlzGetMouseFocusUnit. Таймером можно проверять, что за юнит у игрока выделен благодаря константе BlzGetMouseFocusUnit. Выделен можно только один какой то. Все профит. Но однако, как будут работать функции сокрытия. События мыши есть в 1.27?
есть события наведения мыши, они выводят мировые координаты. Сравниваешь, где лежит точка. Лежит ли в ректе. Все профит.
для таких случаев рекомендуется сначала фрейм-маску на весь экран сделать, так клик пройдет не по точке на экране, а по фрейму. И прицел тогда не закроется. Но координаты все равно можно получить, они даже через интерфейс меняются. Иначе, не сделав этого, прицел будет закрываться при нажатии. А так нажал = а тут сообщение: вы не можете здесь кликать
ловим каст => значит успешна
надо будет потом пример сделать
можно ли как то скрыть круг выбора с этого юнита
можно, но это будет визуально на всех работать.Есть команды отключения круга, но там кажись на всех работает.
код
это новые рефорджовские. Есть еще и старые помню в 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, если на никакого юнита не навел игрок, значит, переменная пуста.
для таких случаев рекомендуется сначала фрейм-маску на весь экран сделать
также помогает фрейм не выделять объекты на карте. И поэтому не увидите подсказки, и что юнит BlzGetMouseFocusUnit выделен. Это больше подходит для заклинании с точкой
В рефордже перестают адекватно работать сокрытие через импорт, или ро работает 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
Не знаю когда у вас наполняется и как. Но судя добавлением. Да есть там такой баг, когда итем просто пропадает и все. Такое было. Примерно при повторном добавлении только встречал. Причинах в этих пополняющих стэках магаза. Факов еще помогал мне
Как превышают лимит скорости атаки в 400%?
там спец нативкой время cooldawn, или по вашему BAT можно уменьшать. Она не зависит от процкентов. Проценты придумали для этих.
лимит 400% можно превысить всякими баффами абилок на скорость. но за точность инфы не ручаюсь. Надо самому протестить. Возможно 400% это предел.
Как снижают длительность бафов/дебафов?
можно через фреймы. или через таймером, кастомизацию баффов тоже можно было делать и в 1.26 без мемов. Еще помню, раньше заменяли способности. Возможна можно задать длительность абилкам через филды. Но напомню, что нельзя всем изменять.
Как увеличивают вероятность срабатывания способностей?
смотря какие способности. чаще через триггеры через рандом.
изменить тип атаки? тогда не будут стрелять через ограды. Когда обойдет ограду, то снимай/выдай классификацию, и вау-ля можно снова атаковать.
или нужен блок? можно сделать триггерно движение снарядов, когда снаряд врезается об щит ментально, делаем отскок, удаляем. если это атака, то проще сделать триггерно атаку, у одного мастера можно узнать. xgm.guru/user/rsfghd если если эта какая то способность, то мб проще сделать триггерно абилку снарядов.
» WarCraft 3 / Происхождение (Конкурс ландшафта 2020)
» WarCraft 3 / [Конкурс ландшафта 2020] "Похоронный звон"
» Война Готики / Война Готики
» Администрация XGM / Можно ли визуально отметить, что это гифка?
Ред. MpW
» WarCraft 3 / Создание пассивки
Ред. MpW
» WarCraft 3 / Создание пассивки
Посмотрел в архиве рефа:
есть файлы по пути units/
hive (как использовать)
hive (пояснения, плюс как женские скины использовать)
» WarCraft 3 / Не найти фреймы в 1.26а
Ред. MpW
» WarCraft 3 / Не найти фреймы в 1.26а
» WarCraft 3 / Не найти фреймы в 1.26а
» WarCraft 3 / Уменьшение здания?
На зданиях лучше указать "Движение - Тип движения" - "Нет", иначе другие типы движения блокируют проходы (если присмотреться у большинства зданий не указан никакой тип движения, кроме эльфийских древ). У эльфийских древ есть способность "Пустить корни", которая делает неподвижными.
Зданиям и декорациям задают еще "Пути - Карта путей" - например текстура "Портал" или отсутствие текстуры ("Нет") делают здание проходимым. Тогда юниты через это свободно перемещаются. Какая-либо другая текстура может блокировать проход. Точно также происходит с декорациями, разрушаемыми декорациями (блокираторами). Об этом можно прочитать здесь
https://www.xgm.guru/p/blog-steal-nerves/ability12
» WarCraft 3 / Не найти фреймы в 1.26а
ui/framedef/ui/reforged/commandbar.fdf - какая-то другая сетка, где кнопок 14
Ред. MpW
» WarCraft 3 / Контроль земли
» WarCraft 3 / Damage per second у снежной бури
Ред. MpW
» WarCraft 3 / Проблемы с begins casting an ability и finishes casting an abily
графика - Анимация: Точка броска и Графика - Анимация: Обратный ход броска
Art - Animation - Cast Point (Real)
Art - Animation - Cast Backswing (Real)
ссылка
Ред. MpW
» WarCraft 3 / Отловить нажатие ЛКМ по области
Ред. MpW
» WarCraft 3 / Отловить нажатие ЛКМ по области
Ред. MpW
» WarCraft 3 / Как убрать иконки передвижения у конкретно взятого юнита?
» WarCraft 3 / Дальнобойные атаки сквозь препятствия
» Unity / Как запустить карту варкрафта в Unity
» WarCraft 3 / Проблема с магазином
» WarCraft 3 / Проблема с магазином
Ред. MpW
» WarCraft 3 / Проблема с магазином
Ред. MpW
» WarCraft 3 / По какому принципу реализована кастомная система статов? Reforge
там спец нативкой время cooldawn, или по вашему BAT можно уменьшать. Она не зависит от процкентов. Проценты придумали для этих.
Ред. MpW
» WarCraft 3 / Дальнобойные атаки сквозь препятствия
или нужен блок? можно сделать триггерно движение снарядов, когда снаряд врезается об щит ментально, делаем отскок, удаляем. если это атака, то проще сделать триггерно атаку, у одного мастера можно узнать. xgm.guru/user/rsfghd если если эта какая то способность, то мб проще сделать триггерно абилку снарядов.