Кстати, постил на хайве краткий "тутор" на эту тему. Скорее в качестве шпаргалки для самого себя, чтобы посмотреть и вспомнить как я это делал, когда забуду
Да в принципе ничего в этой задаче нет невыполнимого или даже архисложного. С учётом того, что формат карт разобран, формат моделей разобран, а архивы варкрафта распаковываются в пару кликов.
Другой вопрос, что никто в здравом уме не сядет это пилить, так что да, на практике нереализуемо.
только костылить с созданием нового юнита либо использовать
Где-то тут на сайте для мгновенного поворота предлагался костыль с переименованием рут кости модели в turret, и триггерным закрепом кости на цели. На рефе так делал для фиксации модельки героя на курсоре, на 1.26 тоже скорее всего рабочий вариант. Но не для каждой задачи этот способ будет допустим.
Большинство моделей этого чела (по крайней мере из данного пака, других не видел) имеют косяк с полигонами, а точнее наличие треугольников с дублирующимися вершинами. Не то чтобы претензия, но я не понимаю, как он вообще смог так моделить. У него это встречается буквально везде, где геометрия не является явной копипастой/небольшой доработкой близзовских моделек. В мдлвисе из точек собирал что ли
Пример. Геосет 0 модели NagaWhaler.mdx имеет 16 таких треугольников из 48
Во-первых, сообщество 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, который в основном близзы использовали в варкрафте, так что либо безье, либо линейная интерполяция.
» Блог Storm'а / Суицидальная атака на Maya API
» Блог Storm'а / Тестим PopcornFX в рефоге
» WarCraft 3 / импорт карт в blender
» WarCraft 3 / как сделать мгновеный поворот юниту
» WarCraft 3 / как сделать мгновеный поворот юниту
Отредактирован Makeba
» WarCraft 3 / как сделать мгновеный поворот юниту
» WarCraft 3 / Заказы на ландшафт
» WarCraft 3 / Как прератить tga файл в png файл?
Первая ссылка по запросу convertio.co/ru/tga-png
» IntelliJASS / BLP
» WarCraft 3 / Проблема с запуском редактора и Jass
» WarCraft 3 / Пак моделей от -Grendel
» WarCraft 3 / Пак моделей от -Grendel
» 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 предлагает проиграть однокадровые гифки