Во-первых, сообщество XGM настоятельно рекомендует перестать использовать эти решения из 2007-го, с даммиками, вызывающими даммиков, сейчас есть возможность сделать лучше. Во-вторых, сообщество XGM настоятельно рекомендует перейти на Ujapi и AngelScript.
Немного говнокода на эту тему, набросал на луа в рефе за 1 наносекунду
group = CreateGroup()
--всякие параметры
range = 650
dAngle = 30
stepAngle = 15
projVelocity = 100
Timers = {}
Timers.bullets = CreateTimer()
TimerStart(Timers.bullets, 0.25, true, function()
launchBullets(GetUnitX(hero), GetUnitY(hero)) -- пусть hero это будет пулемётчик
end)
function launchBullets(x, y) -- выпускаем очередь из пулемёта
local angle = GetUnitFacing(hero)
SetUnitAnimation(hero, "attack")
local segments = getSegments(x, y, range, angle - dAngle, angle + dAngle, stepAngle) --находим конечные точки отрезков, по которым пустим снаряды
local t = CreateTimer()
TimerStart(t, 0.03, true, function()
local p = table.remove(segments, math.random(1, #segments))
launchBullet(x, y, p.x, p.y) --для красоты пускаем выстрелы с небольшой задержкой
if #segments <= 0 or IsUnitDeadBJ(t) then
DestroyTimer(t)
return
end
end)
end
function launchBullet(x, y, x1, y1)
-- выпускаем "снаряд" и ищем цель
local eff = AddSpecialEffect("bullets", x, y)
BlzSetSpecialEffectZ(eff, 60)
BlzSetSpecialEffectYaw(eff, getAngle(x, y, x1, y1))
local t = CreateTimer()
local isProjectileAlive = true
local xEffect, yEffect = x, y
TimerStart(t, 0.015, true, function()
GroupEnumUnitsInRange(group, xEffect, yEffect, 120)
while true do
local target = FirstOfGroup(group)
if target == nil then break end
GroupRemoveUnit(group, target)
if UnitAlive(target) and target ~= hero and IsUnitInRangeXY(target, xEffect, yEffect, 50) --ищем юнитов в ренже, кроме пулемётчика
then
DestroyEffect(eff)
isProjectileAlive = false --после первого столкновения уничтожим снаряд, чтобы он не летел дальше
UnitDamageTargetBJ(hero, target, 20.00, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL) -- наносим те самые 20 дальнего урона
end
end
-- двигаем снаряд в нужном направлении
local sin, cos = getSinCos(xEffect, yEffect, x1, y1)
xEffect = xEffect + cos * projVelocity
yEffect = yEffect + sin * projVelocity
if getDistance(x, y, xEffect, yEffect) >= range or not isProjectileAlive then
DestroyTimer(t)
DestroyEffect(eff)
return
end
BlzSetSpecialEffectPosition(eff, xEffect, yEffect, 60)
end)
end
function getSegments(cx, cy, r, angle1, angle2, angle_step)
local segments = {}
local n = math.floor((angle2 - angle1) / angle_step) + 1
for i = 0, n - 1 do
local angle = angle1 + i * angle_step
local x = cx + r * math.cos(math.rad(angle))
local y = cy + r * math.sin(math.rad(angle))
table.insert(segments, {x = x, y = y})
end
return segments
end
function getSinCos(x1, y1, x2, y2)
local dX = x2 - x1
local dY = y2 - y1
local dist = math.sqrt(dX*dX + dY*dY)
local sin = dY / dist
local cos = dX / dist
return sin, cos
end
function getAngle(x1, y1, x2, y2)
local deltaX = x2 - x1
local deltaY = y2 - y1
local angle
if deltaX == 0 then
if deltaY > 0 then
angle = math.pi / 2
else
angle = -math.pi / 2
end
else
angle = math.atan(deltaY / deltaX)
if deltaX < 0 then
angle = angle + math.pi
end
end
return angle
end
function getDistance(x1, y1, x2, y2)
local deltaX = x2 - x1
local deltaY = y2 - y1
return math.sqrt(deltaX^2 + deltaY^2)
end
Пример использования, с эффектами и набигающими врагами
Раз уж мы всем хгмом делаем наработку для крафтов, то вкачусь тоже В луа я бы сделал так. Ну, если все предметы разные, без повторений.
RequiredItems = {0x6F646566, 0x6F736C6F, 0x6F666972, 0x6F636F72, 0x6F76656E, 0x4930304C}
function checkItems(u)
for _, v in ipairs(RequiredItems) do
if not UnitHasItemOfTypeBJ(u, v) then
return false
end
end
return true
end
Сразу хочу сказать, что события фреймов в рефе багованные, после патча 1.33 вдвойне багованные, здесь каждый выкручивается как может, но без компромиссов тут не обойтись, как и при любой разработке на рефе чего-то интереснее базовой милишки. Вроде в этой карте автор что-то смог толково сделать, рекомендую посмотреть магазин/инвентарь там. Ещё есть вариант перекатиться на южапи, отсутствие проблем там никто вроде не гарантирует, но там есть перспективы в развитии апи, в отличие от рефа, где разрабы заняты фиксом нежити.
По-моему, событие клика сейчас не особо жалуют, вроде делают через Enter и Leave, но я особо не в курсе.
Я пробую магазин делать я заранее сделал переменные для нужных мне предметов накидал код и заметил что под каждую кнопку нужно функцию клика делать и прикинул что код станет гигантским из за этого.
Нам нужен цикл. Создать какую-то базовую функцию для создания кнопки, вызвать её нужное количество раз, и у нас будет нужное количество кнопок.
Не обязательно на каждую кнопку делать свой триггер, и свою функцию обработки события, это можно сделать в одном триггере, и в одной функции, всё что нужно — это повесить на каждую кнопку своё событие. Естественно, событие можно добавить прямо в этом же цикле, где мы создаём кнопки. В триггере останется лишь получить триггернувшийся фрейм, и узнать связанный с ним предмет, который, естественно, нужно связать заранее, например в массиве.
Набросаю пример на луа, ибо не умею в джасс. Ну это так, база, в мультиплеерную карту как готовое решение это не пойдёт, да и в синглплеерную тоже из-за сломанных в рефе сейвов, чивоуштам. На каждую кнопку создал четыре фрейма. Для отслеживания клика используем тип "BUTTON", его прикроем сверху типом "BACKDROP". Ну и два текстовых фрейма для тултипа и стоимости.
код
-- Список доступных для продажи предметов, 15 штук
ItemDB = {'afac', 'spsh', 'ajen', 'bgst', 'belv', 'bspd', 'cnob', 'ratc','rat6','rat9', 'clfm', 'clsd', 'crys', 'dsum', 'rst1'}
FrameToItem = {} -- в будущем заполним эту таблицу, связав фреймы с кодами итемов
Buttons = {} -- в будущем заполним эту таблицу, связав фреймы со стоимостью итемов
CancelShopFrame = nil -- тут будет кнопка выхода из магазина
function Init()
hero = CreateUnit(GetLocalPlayer(), FourCC('Hpal'), 0, 0, 0) --герой, которому будем выдавать предметы
SetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_GOLD, 30000) --накинем голды
initShop() --создадим магазин
end
function initShop()
local size = 0.03 --размер кнопки
local startX, y = 0.5, 0.5 --начальная позиция
local rows, columns = 4, 4 --количество рядов и столбов
local deltaX, deltaY = 0.05, -0.05 --расстояние между кнопками
local counter = 1 --счётчик предметов
local triggor = CreateTrigger()
for r = 1, rows do --ряды
y = y + deltaY
for c = 1, columns do --столбцы
local x = startX + deltaX * (c - 1)
if counter >= 16 then -- 16-ую позицию использует для кнопки выхода
CancelShopFrame = createButton(x, y, size, "ReplaceableTextures\\CommandButtons\\btncancel", "Exit shop", "")
BlzTriggerRegisterFrameEvent(triggor, CancelShopFrame, FRAMEEVENT_CONTROL_CLICK)
break
end
local item = ItemDB[counter] -- получили равкод итема
local text, path = getItemInfo(item)
local cost = math.random(1, 7000) -- в моём примере цена будет рандомная
local button = createButton(x, y, size, path, text, ""..cost)
BlzTriggerRegisterFrameEvent(triggor, button, FRAMEEVENT_CONTROL_CLICK)
FrameToItem[button] = item --записали пару "фрейм = равкод предмета"
Buttons[button] = cost --записали пару "фрейм = стоимость предмета"
counter = counter + 1
end
end
TriggerAddCondition(triggor, Condition(clickToShopFrame))
end
function getItemInfo(id) --вернёт иконку и тултип предмета из РО
--увы, это можно сделать только создав экземпляр предмета, в идеале эти данные ещё бы записать куда, а можно и заранее написать прямо в коде
local item = CreateItem(FourCC(id), 10000, 10000)
local text = BlzGetItemTooltip(item)
local path = BlzGetItemIconPath(item)
RemoveItem(item)
return text, path
end
function clickToShopFrame() -- здесь обрабатываем событие из триггора
local frame = BlzGetTriggerFrame()
resetFrame(frame) --клик по фрейму блокирует события клавы, так что такой костылёк может пригодиться, но не обязательно
if frame == CancelShopFrame then
for k, v in pairs(Buttons) do
BlzFrameSetVisible(k, false)
end
BlzFrameSetVisible(frame, false)
return
end
sellItem(frame)
end
function resetFrame(fr)
BlzFrameSetEnable(fr, false)
BlzFrameSetEnable(fr, true)
end
function sellItem(frame)
-- Здесь мы проверяем голду и делаем что-нибудь с предметом
local gold = PLAYER_STATE_RESOURCE_GOLD
local currentGold = GetPlayerState(GetLocalPlayer(), gold)
local cost = Buttons[frame] --получили цену предмета
if cost <= currentGold then
SetPlayerState(GetLocalPlayer(), gold, GetPlayerState(GetLocalPlayer(), gold) - cost)
local item = CreateItem(FourCC(FrameToItem[frame]), 10000, 10000)
if not UnitAddItem(hero, item) then -- если в инвентаре нет места, то создаём рядом
SetItemPosition(item, GetUnitX(hero), GetUnitY(hero))
end
outputItemInfo(item, cost)
return
end
print "Not enough gold!"
end
function outputItemInfo(item, cost)
print("Received a |c0000FF80"..GetItemName(item).. "|r worth |c00FFFF00"..cost.."|r gold")
end
function createButton(x, y, size, iconPath, tooltipText, cost)
-- Достаточно одного фрейма-кнопки, но для красоты и функциональности можно добавить всё что угодно
-- В данном случае создаёт кнопку, иконку-бекдроп, тултип и текст фрейм для отображения стоимости
local button = BlzCreateFrameByType("BUTTON", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "ScoreScreenTabButtonTemplate", 0)
local icon = BlzCreateFrameByType("BACKDROP", "", button, "", 0)
BlzFrameSetAllPoints(icon, button) --икнока будет полностью закрывать кнопку
BlzFrameSetAbsPoint(button, FRAMEPOINT_CENTER, x, y)
BlzFrameSetSize(button, size, size)
BlzFrameSetTexture(icon, iconPath, 0, false)
local tooltip = BlzCreateFrameByType("TEXT", "", button, "", 0)
BlzFrameSetTooltip(button, tooltip)
BlzFrameSetPoint(tooltip, FRAMEPOINT_BOTTOMLEFT, button, FRAMEPOINT_TOP, 0, 0)
BlzFrameSetEnable(tooltip, false)
BlzFrameSetText(tooltip, "|c00FFFF00"..tooltipText.."|r")
local costFrame = BlzCreateFrameByType("TEXT", "", button, "", 0)
BlzFrameSetPoint(costFrame, FRAMEPOINT_TOPLEFT, button, FRAMEPOINT_BOTTOMLEFT, 0, 0)
BlzFrameSetPoint(costFrame, FRAMEPOINT_BOTTOMRIGHT, button, FRAMEPOINT_BOTTOMRIGHT, 0, -0.01)
BlzFrameSetEnable(costFrame, false)
BlzFrameSetText(costFrame, cost)
BlzFrameSetTextAlignment(costFrame, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_MIDDLE)
return button, icon, tooltip, costFrame
end
Переключение между клипами чёт вообще колхозноватенько реализовано в майе, по сравнению с тем же блендером. Хотя может это я неправильно делаю, ибо в интерфейсе майи разбирался по шортсам с ютуба, кек
Ещё неожиданным приколом оказалось, что Майа не умеет в 3дмаксовский TCB, который в основном близзы использовали в варкрафте, так что либо безье, либо линейная интерполяция.
Чисто по ощущением, кажется, что не зафиксирована бинд поза. Если все анимации раскиданы на одной линейке, и отмечены маркерами, то в нулевом кадре (до начала анимаций) нужно для всех костей зафиксировать LocRotScale.
Но в твилаковском плагине давно уже есть вариант с экспортом анимаций из экшонов, то есть один Action — одна анимация, и с километровой линейкой можно не возиться
Набросал видос с процессом импорта. Это не то чтобы гайд или руководство к действию, просто обозначить суть. Видно что частицы летят немного не туда, думаю из-за неправильных осей.
Working prompt for choosing installation/resource folder
From _b:
Mapping nodes when importing Animation now shows most Nodes instead of only Bones
Selection Box now uses color from settings
Added "unselectable" to the table in GeosetOverviewPanel
Fixed some issues with editing ExtLog
The Internal browser no longer resets the camera when changing model
Added "Remove If Less Than", "Snap To Step" and "Adjust influence for selected" to SkinningOptionPanel
Fixed ReplaceBonesAction not being undoable
Fixed KeyframeActionHelpers not checking timeline.getEntryMap != null
Added QuickDismiss popup to ModelLoader for when a file isn't found or filetype not supported
Added a few filetypes to be opened with FileViewer
Fixed saving settings (now human-readable)
Removed loading of legacy settings
Added checkbox for Optimize On Save to the file menu
Added "Cut Edge" to TwilacsTools
Added a checkbox for "Preserve Animations" panel on the right when editing T-Pose
The "show popup menu"-button is now located in a slightly better spot
(Hopefully) fixed selection in perspective viewport being off...
Export Animations now has a popup with some options
Fixed some instances of selection info not updating properly
Fixed changing mode from ANIMATE while playing animation continuing playing the animation
Undoing deleting nodes will now put them back in the original order
Undoing deleting the last vertices of geosets will now put the geosets back in the original order
From _a:
Fixed issue where redoing adding keyframes would accumulating transforms
Fixed issues with animating Scaling
Merge Geosets now has a more intuitive UI, and puts geosets back in the correct order when undoing
Added an option to choose rotation axis to Rotate Model
Made Rotate Model now rotates (non uniform) scale-values better
ImportPanel now adds GlobalSeqs and used ribbon materials
Simplified TimeBoundChooserPanel by removing deprecated stuff
Simplified the add new Animation popup by removing unnecessary stuff
Fixed layer filter mode Blend not rendering properly
Fixed Duplicate Animation not copying Geoset Extents
Cleaned up ReorderAnimationsPanel and added options to set when the first animation should be placed and align the start times of the following animations to rounded times
Changed some list rendering
Can now paste stuff with global sequences, and will match animations only by uppercase alphanumerical name
Removed some non-functioning menu settings
Edit flip-book textures as text now does a better job parsing file paths
Added some model loading messages
Added setting for default file extension filter
Cleaned up the preferences window
GifExportHelper no longer throws an error when trying to export a static pose
RecalculateExtents now fully undoable
Changing Interp Type now properly undoable after redo
Changing Material ShaderString to an SD-string no longer doubles all layers
Fixed a bug preventing saving flip-book materials
Fixed NodesOverviewPanel not showing Helper count
Added save and load buttons to the ShaderEditPanel
Made some changes to maybe render transparent geosets better
Can now play .flac event sounds
Can now propagate expansion by holding [ctrl] for MPQImageBrowser and MPQBrowser
The ImageBrowser now has a filter-menu for image type
Added some markers to Texture names to warn if the filepath is sus
Added a button to ComponentPopcornPanel for exporting with ".pkb" as file extension
Added checkboxes for dropShadow and unselectable to ComponentGeosetPanel
Can now parse Reforged sound lookup to display sound names for EventObject
Prettier names for Materials in lists
GeosetAnim will now properly save static alpha
Will now properly save GeosetAnim.staticColor=[0,0,0] when saving as .mdl
Added MaterialOverviewPanel
Wrapped Particle and Particle2 specific settings in a collapsable panel
Made Create Back2BackAnimation undoable
ScaleSequencesLengthsAction will now properly put things back on undo
Added some more options in the SkinningOptionPanel
Имха, сейчас в этом нет особого смысла, ибо карту "Мы – те самые старые Близзард, которых вы любили в детстве, а не эти корпоративные повесточные активижоновские мелгомягкие болваны, которых вы ругаете в интернете" они уже успешно разыграли, кикстартер тому подтверждение.
» StarCraft 2 / .m3 аддон для Blender
» WarCraft 3 / Пулемётный огонь веером
Отредактирован Makeba
» WarCraft 3 / Пулемётный огонь веером
Во-вторых, сообщество XGM настоятельно рекомендует перейти на Ujapi и AngelScript.
» WarCraft 3 / Массив в магазине.
» WarCraft 3 / Где находятся спецэффекты, появляющиеся на земле
» WarCraft 3 / Оптимизация Выполнения Последовательности Кода
Отредактирован Makeba
» WarCraft 3 / Оптимизация Выполнения Последовательности Кода
Раз уж мы всем хгмом делаем наработку для крафтов, то вкачусь тожеВ луа я бы сделал так. Ну, если все предметы разные, без повторений.Отредактирован Makeba
» WarCraft 3 / Массив в магазине.
Не обязательно на каждую кнопку делать свой триггер, и свою функцию обработки события, это можно сделать в одном триггере, и в одной функции, всё что нужно — это повесить на каждую кнопку своё событие. Естественно, событие можно добавить прямо в этом же цикле, где мы создаём кнопки. В триггере останется лишь получить триггернувшийся фрейм, и узнать связанный с ним предмет, который, естественно, нужно связать заранее, например в массиве.
Отредактирован Makeba
» Блог Storm'а / Суицидальная атака на Maya API
» Администрация XGM / XGM предлагает проиграть однокадровые гифки
Автозагрузка видосов с ютуба раздражает, кстати
» Таверна "Ржавое колесо" / Как покрасить яйца луковой шелухой или каркаде
» Администрация XGM / XGM предлагает проиграть однокадровые гифки
» Администрация XGM / XGM предлагает проиграть однокадровые гифки
» Warcraft 3: The Lord Of The Rings / удалить
» WarCraft 3 / Экспорт модели из Блендера в Mdl формат
» Блог Storm'а / Тестим PopcornFX в рефоге
» Блог Storm'а / Тестим PopcornFX в рефоге
» Блог Storm'а / Тестим PopcornFX в рефоге
» Unryze Jass API / UjAPI - AngelScript
» WarCraft 3 / RMS Twilac Edition
Отредактирован Makeba
» Unryze Jass API / UjAPI - AngelScript
» Unryze Jass API / UjAPI - AngelScript
Отредактирован Makeba
» Прочее / Сэмми Дидье вкатился в Stormgate
» Прочее / Первый взгляд на редактор кастомок Stormgate
Отредактирован Makeba
» Прочее / Сэмми Дидье вкатился в Stormgate