компилирует war3map.j какой-то утилитой в составе JNGP
JassHelper эта утилита, её можно через PowerShell отдельно от JNGP запускать.
Так-то можно даже просто pjass.exe (тоже в составе JNGP) запустить (через PowerShell), ведь в war3map.j код уже в JASS.
БОльшая часть строк находится в war3map.wts, его и нужно переводить, там находятся все описания способностей, юнитов, предметов и так далее, иногда даже просто мусор, который редактор забыл убрать.
Дам совет, у способностей нужно переводить только описания, имена в самой игре всё равно не отображаются. А ещё на них может быть завязан код (как у меня в карте), так что лучше их вообще не трогать.
С юнитами и предметами возможна такая же ситуация, но вот тут сложнее, ведь их имена отображаются в игре.
Потому перед переводом, нужно проверить war3map.j на наличие функций GetObjectName, GetItemName, GetUnitName. Если они есть, то понять, для чего они используются, а потом уже переводить.
Часть строк может быть в war3map.j, многие и переводить не нужно (пути к эффектам, например), но вот выводимые на экран сообщения перевести стоит.
Кстати, а можно ли сделать карту с различными шириной и длиной?
Да, это можно сделать даже в меню создания новой карты.
Включи среднюю сетку, размер белого квадрата есть 128х128, и именно в них и измеряется размер карты, то есть, выражаясь точнее, когда увеличиваешь или уменьшаешь карту, нужно сделать количество белых квадратов по длине и ширине кратным 32.
Делать в if действие только в else плохой тон и просто неудобно для чтения кода. Тут всё равно идёт сравнение логических, потому данное исправление улучшит читаемость кода и никак не повлияет на производительность.
Событие: каждые 20 секунд.
Действия: поставить переменной B значение ноль.
Выбрать все предметы в области Х, равкод которых Z, и для каждого сделать Поставить переменной B значение B + 1.
Если B равно 0 создать предмет иначе ничего не делать.
У меня к каждому юниту крепится ссылка на объект структуры, в которой есть отдельные списки для разных типов баффов. Если же бафф имеет два разных эффекта, то ссылка на него хранится в двух списках. При удалении конфликтов не возникает, потому что при удалении бафф удаляет все ячейки, которые были к нему привязаны, а ячейки удаляются линейно.
Ячейка списка
struct MBSNode
readonly MinionBuff buf
readonly integer number
thistype prev = 0
thistype next = 0
method basicInit takes MinionBuff buf, integer number returns nothing
static if ErrorDetectionEnabled_MBSNode then
if this == 0 then
call ErrorOccurred("Error: MBSNode struct has run out of indexes. Current amount of indexes is 8190", "MBSNodeError")
endif
endif
set this.buf = buf
set this.number = number
endmethod
method removeFromList takes nothing returns nothing
if prev != 0 then
set prev.next = next
endif
if next != 0 then
set next.prev = prev
endif
endmethod
endstruct
struct MBSNodeReal extends MBSNode
real value = 0.
endstruct
struct MBSNodeInt extends MBSNode
integer value = 0
endstruct
struct MBSNodeIntNReal extends MBSNode
integer value = 0
real rvalue = 0.
endstruct
Структура, в которой содержится списки, называется MBS, в ней есть счётчик количества баффов, это нужно для определения, какой бафф был добавлен раньше.
Списки в основном задаются таким макросом.
Список
// Name - имя типа баффа
// ntyp - тип ячейки
// values - если есть значения у баффа
// bool - если значения типа boolean
// null - нуль для заданного типа значений.
//! textmacro MBSList takes Name, ntyp, values, type, bool, null
private MBSNode$ntyp$ head$Name$
readonly integer buffsAmount$Name$ = 0
method add$Name$Buff takes MinionBuff obj returns MBSNode$ntyp$
local MBSNode$ntyp$ node = MBSNode$ntyp$.create()
if buffsAmount$Name$ == 0 then
set head$Name$ = node
else
set node.next = head$Name$
set head$Name$.prev = node
set head$Name$ = node
endif
set BuffCounter = BuffCounter + 1
set buffsAmount$Name$ = buffsAmount$Name$ + 1
call node.basicInit(obj, BuffCounter)
//call DebugMsg("Current amount of $Name$ buffs is " + I2S(buffsAmount$Name$) + ".")
return node
endmethod
method remove$Name$Buff takes MBSNode$ntyp$ node returns nothing
set buffsAmount$Name$ = buffsAmount$Name$ - 1
if head$Name$ == node then
set head$Name$ = node.next
endif
static if $values$ then
static if $bool$ then
set booleanAmount$Name$ = booleanAmount$Name$ - node.value
set total$Name$Bonus = booleanAmount$Name$ > 0
else
set total$Name$Bonus = total$Name$Bonus - node.value
endif
endif
call node.removeFromList()
call node.destroy()
//call DebugMsg("Current amount of $Name$ buffs is " + I2S(buffsAmount$Name$) + ".")
endmethod
static if $values$ then
static if $bool$ then
private integer booleanAmount$Name$ = 0
readonly boolean total$Name$Bonus = false
else
readonly $type$ total$Name$Bonus = $null$
endif
method change$Name$Value takes MBSNode$ntyp$ node, $type$ value returns nothing
static if $bool$ then
local integer v = B2I(value)
set booleanAmount$Name$ = booleanAmount$Name$ - node.value + v
set total$Name$Bonus = booleanAmount$Name$ > 0
set node.value = v
else
set total$Name$Bonus = total$Name$Bonus - node.value + value
set node.value = value
endif
endmethod
endif
//! endtextmacro
В этой же системе определяется текущее кол-во брони, уязвимый ли он или нет, уровень замедления или ускорения, и пр.
При удалении объекта системы удаляются и все навешанные баффы.
Удаление
// Макрос ниже также используется для удаления баффов конкретного типа при каких-то обстоятельствах.
// pos - бафф положительный.
// Ячейки удаляются с удалением баффа.
//! textmacro DeleteBuffs takes Name, first, pos
static if $first$ then
local MBSNode node = head$Name$
local MBSNode temp
else
set node = head$Name$
endif
loop
exitwhen node == 0
set temp = node.next
$pos$if node.buf.buffdata.negative then
call node.buf.destroy()
$pos$endif
set node = temp
endloop
//! endtextmacro
...
method onDestroy takes nothing returns nothing
//! runtextmacro DeleteBuffs("EoT", "true", "//# ")
//! runtextmacro DeleteBuffs("Shield", "false", "//# ")
//! runtextmacro DeleteBuffs("MoveSpeed", "false", "//# ")
//! runtextmacro DeleteBuffs("Stun", "false", "//# ")
//! runtextmacro DeleteBuffs("Persistence", "false", "//# ")
//! runtextmacro DeleteBuffs("PhysArmor", "false", "//# ")
//! runtextmacro DeleteBuffs("MagcArmor", "false", "//# ")
//! runtextmacro DeleteBuffs("Armor", "false", "//# ")
//! runtextmacro DeleteBuffs("PhysImmune", "false", "//# ")
//! runtextmacro DeleteBuffs("MagcImmune", "false", "//# ")
//! runtextmacro DeleteBuffs("Protection", "false", "//# ")
//! runtextmacro DeleteBuffs("Invul", "false", "//# ")
call DebugMsg("MinionBuffStorage " + I2S(this) + " is deleted.")
endmethod
Сами баффы.
Каждый бафф - отдельная структура, которая наследуется от общей.
Бафф
struct MinionBuff
integer typ
Minion target
unit source
CustomPlayer owner
MinionBuffData buffdata
integer currticks = 0
boolean isRunning = false
MBSNode node1
MBSNode node2
MBSNode node3
//! runtextmacro CustomTimer("t", "Timer", "MinionBuff", "false")
method onDestroy takes nothing returns nothing
call DeleteTimer()
call RemoveSavedInteger(Hash, target.id, typ)
call UnitRemoveAbility(target.minion, buffdata.effectid)
set source = null
call DebugMsg("MinionBuff " + I2S(this) + " is deleted.")
endmethod
method basicInit takes Minion host, unit src, CustomPlayer p, integer T returns nothing
static if ErrorDetectionEnabled_MinionBuff then
if this == 0 then
call ErrorOccurred("Error: MinionBuff struct has run out of indexes. Current amount of indexes is 8190", "MinionBuffError")
endif
endif
set typ = T
set target = host
set source = src
set owner = p
set buffdata = p.minion_buffdata[T]
call InitTimer()
call SaveInteger(Hash, target.id, T, this)
//call DebugMsg("MinionBuff " + I2S(this) + " is created.")
endmethod
...
endstruct
Может быть всего 3 разных типа эффекта у баффа, пока максимум было 2.
Макрос InitBuff инициализирует любой бафф, но вопрос не об устройстве баффов, потому опустим его полное устройство. При вызове баффа (метод launch) делаются некоторые проверки, если всё хорошо, то создаётся объект и вызывается его метод onCreate, где сохраняются ссылки на его ячейки.
Это всё работает благодаря одной лишь способности, известной как "тёмный".
Тёмный (Chaos) у паровых танков, у охотников за головами своя аналогичная способность.
Имя Chaos пошло от таких же способностей для орков, которые превращаются в одержимых в одной миссии, потому что паровые танки и берсеркеры были добавлены в TFT.
Проверку на неуяз можно сделать такую, как и говорил quq_CCCP.
function IsUnitInvulnerable takes unit u return nothing
local unit d = CreateUnit(GetOwningPlayer(u), DUMMY_CHECKER, GetUnitX(u), GetUnitY(u), 0.)
local boolean b = IssueTargetOrderById(d, Order_attack, u)
call RemoveUnit(d)
set d = null
return b
endfunction
В функции main по умолчанию есть вызов стандартной функции InitBlizzard, в которой есть вызов функции InitSummonableCaps.
function InitSummonableCaps takes nothing returns nothing
local integer index
set index = 0
loop
// upgraded units
// Note: Only do this if the corresponding upgrade is not yet researched
// Barrage - Siege Engines
if (not GetPlayerTechResearched(Player(index), 'Rhrt', true)) then
call SetPlayerTechMaxAllowed(Player(index), 'hrtt', 0)
endif
// Berserker Upgrade - Troll Berserkers
if (not GetPlayerTechResearched(Player(index), 'Robk', true)) then
call SetPlayerTechMaxAllowed(Player(index), 'otbk', 0)
endif
// max skeletons per player
call SetPlayerTechMaxAllowed(Player(index), 'uske', bj_MAX_SKELETONS)
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
Тут выставляется лимит на обычных скелетов, убираются иконки парового танка и берсерка.
Перед выполнением InitBlizzard выполняются все функции, что генерируются в настройках игроков, для этого в InitSummonableCaps стоят проверки.
А вот интересно, когда делаешь грейд на берсерков, то в бараке нанимаешь троллей или берсерков?
Достаточно взглянуть на грейд и становится ясно, что нанимаешь сразу берсерков.
Решение вопроса такое.
1. Во время инициализации ставим для всех игроков (как в функции InitSummonableCaps) разрешаемое кол-во рыцарей на 0.
2. Один триггер регистрирует появление алтаря на карте.
С: Юнит входит в область Вся карта
У: Тип Triggering unit равно Алтарь
Д: Поставить лимит на производство Рыцарь в -1 для Игрока-владельца Triggering Unit
3. Другой триггер регистрирует смерть алтаря.
С: Юнит умирает
У: Тип Умирающий юнит равно Алтарь
Д: Выбрать всех юнитов в группе (Юниты Игрока-владельца Умирающего юнита с условием (Тип Matching unit равно Алтарь))
Если первый юнит в последней созданной группе равно Нет юнита то (Поставить лимит на производство Рыцарь в 0 для Игрока-владельца
Умирающего юнита)
Уничтожить последнюю созданную группу.
тебе точно нужно отследить EVENT_WIDGET_DEATH, и ни фреймом раньше, иначе бага. Так же получение других приказов на основе стана для взаимодействия с морфами.
Ну у меня у юнита есть ссылки на все прикреплённые объекты, т. о. при смерти все навешанные баффы удаляется, а сами баффы сделаны на таймерах.
» WarCraft 3 / Движение, jass
» WarCraft 3 / Помогите разобраться в карте, в крафтах!
» WarCraft 3 / Движение, jass
» WarCraft 3 / Как сделать возраждение?
Ред. PT153
» WarCraft 3 / Помогите разобраться в карте, в крафтах!
Так-то можно даже просто pjass.exe (тоже в составе JNGP) запустить (через PowerShell), ведь в war3map.j код уже в JASS.
Дам совет, у способностей нужно переводить только описания, имена в самой игре всё равно не отображаются. А ещё на них может быть завязан код (как у меня в карте), так что лучше их вообще не трогать.
С юнитами и предметами возможна такая же ситуация, но вот тут сложнее, ведь их имена отображаются в игре.
Потому перед переводом, нужно проверить war3map.j на наличие функций GetObjectName, GetItemName, GetUnitName. Если они есть, то понять, для чего они используются, а потом уже переводить.
Часть строк может быть в war3map.j, многие и переводить не нужно (пути к эффектам, например), но вот выводимые на экран сообщения перевести стоит.
» WarCraft 3 / Как сделать возраждение?
Да, без локальной можно.
Ред. PT153
» WarCraft 3 / Варианты границ карты
Включи среднюю сетку, размер белого квадрата есть 128х128, и именно в них и измеряется размер карты, то есть, выражаясь точнее, когда увеличиваешь или уменьшаешь карту, нужно сделать количество белых квадратов по длине и ширине кратным 32.
Ред. PT153
» WarCraft 3 / Варианты границ карты
Ред. PT153
» WarCraft 3 / Как сделать возраждение?
» WarCraft 3 / Динамический триггер ?!
» WarCraft 3 / Аттач группы триггеров
» WarCraft 3 / Как сделать триггер по типу..
» WarCraft 3 / Как сделать триггер по типу..
Ред. PT153
» WarCraft 3 / Как сделать триггер по типу..
» WarCraft 3 / Как сделать триггер по типу..
» WarCraft 3 / Построение фигур
Atesla:
Ред. PT153
» WarCraft 3 / Как сделать триггер по типу..
Действия: поставить переменной B значение ноль.
Выбрать все предметы в области Х, равкод которых Z, и для каждого сделать Поставить переменной B значение B + 1.
Если B равно 0 создать предмет иначе ничего не делать.
Ред. PT153
» WarCraft 3 / Построение фигур
Ред. PT153
» WarCraft 3 / Аттач группы триггеров
Списки в основном задаются таким макросом.
При удалении объекта системы удаляются и все навешанные баффы.
Каждый бафф - отдельная структура, которая наследуется от общей.
» WarCraft 3 / WE (JNGP) и английская винда
» WarCraft 3 / WE (JNGP) и английская винда
Ред. PT153
» WarCraft 3 / Как скрыть иконки недоступных для обучения юнитов из здания?
Имя Chaos пошло от таких же способностей для орков, которые превращаются в одержимых в одной миссии, потому что паровые танки и берсеркеры были добавлены в TFT.
Ред. PT153
» WarCraft 3 / Вопрос по механике вампиризма (триггерного) и отложению урона
Ред. PT153
» WarCraft 3 / Как скрыть иконки недоступных для обучения юнитов из здания?
Перед выполнением InitBlizzard выполняются все функции, что генерируются в настройках игроков, для этого в InitSummonableCaps стоят проверки.
8gabriel8:
1. Во время инициализации ставим для всех игроков (как в функции InitSummonableCaps) разрешаемое кол-во рыцарей на 0.
2. Один триггер регистрирует появление алтаря на карте.
Ред. PT153
» WarCraft 3 / Аттач группы триггеров