24

» WarCraft 3 / Официально стал доступен PTR 1.31

Появилась перспективная идея - подсунуть жассхелперу свой парсер вместо pjass и через него запилить работу с Lua во внешнем редакторе.
Возможно идея не самая свежая - тут или на хайве кто-то мог уже это придумать раньше и лучше меня.
Результат первого теста - если оставить жассхелпер и выпилить pjass парсер, то все исправно работает в Lua режиме - пропадает необходимость отключать сам жассхелпер, хоть и толку от него самого мало без pjass парсера, естественно.Но это дает возможность затолкать жассхелперу что-то другое вместо pjass парсера не нарушая работоспособность редактора в Lua режиме.
На очереди второй тест - проверить порядок выполнения парсеров и не тупиковая ли версия кода используется на этапе работы жассхелпера. Пока у меня сложилось впечатление, что сперва генерится стандартный .j файл, со вставками lua кода, а потом этот файл скармливается конвертеру в Lua. Если жассхелпер отрабатывает до конвертера в Lua и результат его работы передается дальше, а не теряется, то кастомный парсер вполне может подтягивать внешний Lua код и подсовывать его в нужное место до конверта всего кода карты в Lua.
А потом останется только написать этот самый парсер который будет втягивать внешние файлы.
Увы, продолжить смогу только вечером, если вобще смогу найти достаточно времени.
24

» WarCraft 3 / Самый производительный Bullet Hell

NazarPunk, первое срабатывание - да, может даже со 100% надежностью, а создание и старт таймера произойдут раньше. Я предпочитаю не рисковать и выношу такие вещи в места которые гарантированно не вызываются слишком рано.
Даже систему модулей/библиотек в итоге себе для этого пилить начал, хотя изначально не собирался сильно этим заморачиваться.
24

» WarCraft 3 / Самый производительный Bullet Hell

NazarPunk, использование анонимной функции в триггере без необходимости это, имхо, лишнее - лишний кложур за собой тащиш. Старт таймера там где он у тебя стартует тоже выглядит опасно - этот код же выполнится еще до триггеров инициализации карты.
24

» WarCraft 3 / Призыв юнитов со способность "Перерождение"

Кабачок, господи, зачем? o_O Если срабатывание перерождения давно научились отслеживать...
24

» WarCraft 3 / Проблема с защитой карты

Лучше при протекте просто выбирать путь под результат протекта отличающийся от того где лежит твоя карта - мороки меньше.
24

» WarCraft 3 / Проблема с защитой карты

Хранить копию не запротекченую у себя и никому её не давать, а запротекченую только выкладывать и не пытаться потом её редактировать.
24

» WarCraft 3 / Проблема с заменой шахт

А ничего что ты координаты где собираешся создавать перезаписываеш постоянно при переборе? Вместо того чтобы взять их прямо перед удалением...
Соответственно, в x y при текущей реализации всегда будут координаты последнего рудника, а не того который был удален...
Логичней было бы не отдавать наружу из перебора координаты вобще, а использовать их только внутри перебора, а потом отдельно брать координаты рудника перед удалением в другие локалки.
24

» WarCraft 3 / Призыв юнитов со способность "Перерождение"

Был вроде костыль с отловом приказа на снятие щита пехотинца при смерти/перерождении.
24

» WarCraft 3 / Изменение огненного столба

Как вариант - ставить в центр столба даммика с аурой замедления и ограниченным временем жизни. Но это тоже только триггерно все.
24

» WarCraft 3 / Переделка кода на Lua

То что ты нашел - это я храню список функций которые вызываются триггером отслеживающим смерти юнитов - чтобы не плодить множество таких триггеров без необходимости и заодно контролировать порядок срабатывания обработчиков смерти когда это понадобится.
В твоем случае - смотря что ты чистить будеш.
Если данные связаные с системой - обнуляеш те переменные в таблице-записи которые использовал и которые не нужны в других местах. data.s = nil data.a=nil и так далее.
Если все данные на юнита (юнит умер или удаляется) - записываем nil по хендлу. HandleData[GetHandleId(u)] = nil.
У меня в либе для ручной чистки я вызвал бы HandleData:Purge(GetHandleId(u)), если бы не полагался на автоматическую чистку по какой-то причине, но там внутри пока все то-же самое присвоение nil.

Для особо тяжелых случаев можно заводить еще дочерние записи в таблице-записи - если какая-то система хранит много данных на одного юнита, особенно если все эти данные временные, то можно ложить их целой таблицей и потом всей таблицей и убивать
data.somebulkdata = { a=42 b=42 c=42}
data.somebulkdata = nil

Еще есть нюанс - для критичных по производительности мест стоит использовать массивы вместо таблиц. По сути те-же таблицы, но с целочисленной нумерацией элементов вместо обращения по имени. Внутренная реализация таблиц оптимизирует такую ситуацию и обращение к элементам происходит быстрее.
24

» WarCraft 3 / Переделка кода на Lua

Чтобы повторно не создавать запись для юнита, а извлечь текущую?
Поскольку я исхожу из того что в одном месте хранится вся привязанная к юниту информация, то да, извлекается старая запись если такая была.
Но не только - еще это нужно чтобы создать запись для юнита если её еще нет. И именно поэтому сыпался твой вариант без этой строки - по хендлу мы вынимали nil (он же null если по джасовски) если на хендл еще ничего не было записано и на этом все падало, естественно, т.к. nil это ни разу не таблица. У меня без этой строки работало потому как у меня в библиотеке переопределена операция получения значения из таблицы HandleData - либа сама создает новые таблицы-записи если на хендл еще ничего не записано.
24

» WarCraft 3 / Переделка кода на Lua

Рекомендую освоить xpcall для отладки, он принимает два аргумента - функцию которую надо выполнить, можно анонимную и функцию в которую будет передана ошибка если что-то пойдет не так при выполнении функции из первого аргумента. В моем первом комменте много вызовов xpcall, посмотри на примере.
24

» WarCraft 3 / Переделка кода на Lua

Bergi_Bear, смотри выше - я в последнем комменте дописал "минимальный" вариант замены моих библиотек, тебе не хватает одной важной строки при работе с данными
if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
24

» WarCraft 3 / Переделка кода на Lua


А еще у меня там местами xpcall отладочный висит, там где я ловил косяки и не убрал его потом, без него код чуть проще становится, он нужен только для отлова ошибок и в моем случае вывода их на экран.

Это печально, новые костыли, а я уже обрадовался, что ничего обнулять и чистить не нужно =(
Не нужно обнулять локалки в функциях и все что ограниченного срока жизни, но всякие системы хранения данных они же либо глобальные либо в "локальном" скопе всей карты и, соответственно, живут пока загружена карта - там вручную подчищать надо, естественно. Можно делать это полу-автоматически как у меня, можно вручную когда становится известно что данные уже не нужны. Можно было бы использовать таблицу со слабыми ключами, но тогда возникает вопрос что использовать в качестве ключей - можно прямо на самого юнита вешать, чтобы когда игра его удалит из памяти, таблица почистилась сборщиком мусора, но нет гарантии что это будет работать и не будет вызывать десинки т.к. сборщику мусора синхронизация не указ.
Bergi_Bear:
но мне можно себе такой вариант забрать или надо ещё что-то дополнительно записать?
У меня библиотека отгорожена только ради того чтобы автоматизировать сборку мусора и создание новых таблиц при обращении по хендлу на который ничего нет.
Минимальный вариант, наверно, такой:
local HandleData = {} -- я использую локальные переменные в скопе карты, при этом важен порядок но доступ к ним идет чуть быстрее чем в глобальном скопе

function ForceUnit (u,a,d,s,flag)
  local data = HandleData[GetHandleId(u)]
  if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
  data.a = a
  data.d = d
  data.s = s
  data.flag = flag
  GroupAddUnit(gforce, u)
end
-- --------------
ForGroup(gforce, function()
  local u=GetEnumUnit()
  local h=GetHandleId(u)
  local data = HandleData[h]
  local a=data.a
  local d=data.d
  local s=data.s
  local flag=data.flag
  ...
end
24

» WarCraft 3 / Переделка кода на Lua

На примере твоего кода - в моем варианте работа с данными будет выглядеть примерно так:
function ForceUnit (u,a,d,s,flag)
  local data = HandleData[GetHandleId(u)]
  data.a = a
  data.d = d
  data.s = s
  data.flag = flag
  GroupAddUnit(gforce, u)
end
-- --------------
ForGroup(gforce, function()
  local u=GetEnumUnit()
  local h=GetHandleId(u)
  local data = HandleData[h]
  local a=data.a
  local d=data.d
  local s=data.s
  local flag=data.flag
  ...
end

Насчет удобства я пока не могу оценить ибо вообще ничего не понимаю, что происходит, но очищать ничего не надо? юнит умер, его записи остались в таблице, сборщик мусора все сделает?
Сборщик сам не настолько крут, поэтому у меня там есть функции Purge и Reset для сноса данных из таблицы, а также целая либа CleanUp для перехвата смертей и удаления юнита, на которую либа хранения данных вешает свой хук в котором и подчищаются данные для таких юнитов.
24

» WarCraft 3 / Переделка кода на Lua

Bergi_Bear, у тебя по таблице под каждое значение, а в моем варианте одна таблица в которой внутри по таблице на каждый хендл на который что-то прикручено. Мой вариант удобней в использовании т.к. все что связано с одним юнитом собрано в одном месте.
24

» WarCraft 3 / Отлов урона на 131 PTR

Bergi_Bear, не знаю, не пробовал. По логике - должны, если урон проходит но отменяется скилом, а как на самом деле только практика покажет.
24

» WarCraft 3 / Отлов урона на 131 PTR

Важно учитывать что на момент срабатывания DAMAGING это полный входящий урон - до срабатывания брони, резистов и всего остального.
24

» WarCraft 3 / Переделка кода на Lua

Два варианта
1 - массив-таблица с индексами по хендлу и еще одной таблицей в качестве значений
2 - передача переменных через кложуры (не твой случай ведь тебе на юнита данные вязать надо т.к. таймер один на всех)
Оба способа есть в примере ниже, если сможеш разобраться в том что там происходит.
немного моего сырого кода из тестовой карты
--========== Common Utils start ====================

local function RAW(id) --есть более эффективный вариант, но пока и так сойдет
  return id:byte(1) * 0x1000000 + 
         id:byte(2) * 0x10000 + 
         id:byte(3) * 0x100 +
         id:byte(4)
end

local function ehandler( err )
  print( "ERROR:", err )
end

--========== Common Utils end ====================
--========= Libraries start ==============

local Libraries = {libs = {}}

function Libraries:Init()
  print("Libraries Init")
  for id,lib in ipairs(self.libs) do
    if (lib.Init) then
      print("Pre Init "..lib.name)
--      lib:Init()
      xpcall(function() lib:Init() end,ehandler)
      print("Post Init "..lib.name)
    else
      print("Skip Init "..lib.name)
    end
  end
  for id,lib in ipairs(self.libs) do
    if (lib.Final) then
      print("Pre Final "..lib.name)
--    lib:Final()
      xpcall(function() lib:Final() end,ehandler)
      print("Post Final "..lib.name)
    else
      print("Skip Final "..lib.name)
    end
  end
end

function Libraries:new(name,dep)
  local lib = {}
  lib.name = name
  lib.dep = dep
  table.insert( self.libs, lib )
  self[name] = lib
  return lib
end

--========= Libraries end ==============
--============ CustomDataLibrary start ============

local DataStorage = Libraries:new("DataStorage",{})
function DataStorage:Reset(h)
  print("data reset "..h)
  self[x] = {}
  return self[x]
end
function DataStorage:Purge(h)
  print("data purge "..h)
  self[h] = nil
end
function DataStorage:new (o)
  o = o or {}
  o.Reset = DataStorage.Reset
  o.Purge = DataStorage.Purge
  setmetatable(o, DataStorage)
  DataStorage.__index = function (table, key) table[key] = {} return table[key] end
  return o
end

local HandleData
local ProtoData

function DataStorage:Init()
    HandleData = DataStorage:new()
    ProtoData = DataStorage:new()
end

function DataStorage:Final()
  local clean = Libraries.CleanUp
  table.insert( clean.DeathHandlers,function(u,h) HandleData:Purge(h) end)
  table.insert( clean.RemoveHandlers,function(u,h) HandleData:Purge(h) end)
end

--============ CustomDataLibrary end ==============
--============ CleanUpLib start ===============

local CleanUp =  Libraries:new("CleanUp",{})
CleanUp.DeathHandlers={}
CleanUp.RemoveHandlers={}
CleanUp.RemoveUnitBase=nil

function CleanUp.OnDeath()
  local unit u = GetTriggerUnit()
  local id = GetHandleId(u)
  for n,h in ipairs(CleanUp.DeathHandlers) do h(u,id) end
end

function CleanUp.OnRemove(u)
  local id = GetHandleId(u)
  for n,h in ipairs(CleanUp.RemoveHandlers) do h(u,id) end
  return CleanUp.RemoveUnitBase(u)
end

function CleanUp:Init()
  local trig = CreateTrigger()
  TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_DEATH )
  TriggerAddAction( trig, CleanUp.OnDeath )

  self.RemoveUnitBase=RemoveUnit
  RemoveUnit = self.OnRemove
end

function CleanUp:Final()
  table.insert( self.DeathHandlers,function(u,h) print("Final Unit Death Handler "..GetUnitName(u)) end)
  table.insert( self.RemoveHandlers,function(u,h) print("Final Unit Remove Handler "..GetUnitName(u)) end)
end

--============ CleanUpLib end ===============
--============ TestLib start ===============

local TestLib = Libraries:new("TestLib",{})

function TestLib.TestGroup(g)
  print("group test start")
  xpcall(function()
    local a = 42
    local i = 0
    ForGroup(g, function()
      xpcall(function()
        local u = GetEnumUnit()
        print('i='..i..' a='..a.." "..GetHandleId(u).." "..GetUnitName(u))
        i = i +1
      end,ehandler)
    end)
  end,ehandler)
  print("group test end")
end

TestLib.TimerCounter=0
function TestLib.TestTimers()
  local t1 = CreateTimer()
  local i = 0
  local a = 42
  local x = TestLib.TimerCounter
  print('start timer '..x)
  TimerStart(t1, 1, true, function()
    print('timer '..x..' tick '..i..' a='..a)
    i = i +1
  end)
  TestLib.TimerCounter = TestLib.TimerCounter+1
end

function TestLib.TestUnit(u)
  print("unit test start")
  print(u)
  local h = GetHandleId(u)
  print("handle "..h)
  local data = HandleData[h]
  if (data.num==nil)then
    data.num=0
  end
  data.num = data.num+1
  BlzSetUnitRealField(u, UNIT_RF_DEFENSE, data.num)

  print("unit test end")
end

--============ TestLib end ===============
24

» WarCraft 3 / Заклинание: Вихрь Иллюзий

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

» WarCraft 3 / Заклинание: Вихрь Иллюзий

Bergi_Bear, файл должен быть .lua чтобы vscode его понимал как lua и включал подсветку и плагины.
24

» WarCraft 3 / Заклинание: Вихрь Иллюзий

Только первый вызов должен быть после инициализации.
Справедливо только для глобальных функций и переменных, а для локальных порядок важен.