|
|
Гайд: как зарегистрировать новый квест
1) Подготовь класс квеста
Принято класть в пакет quests.Q70001_ElmorionCh1 и файл Q70001_ElmorionCh1.java.
[code=java]
// Пример минимального скелета под сборку (Elmorion)
package quests.Q70001_ElmorionCh1;
import org.l2jmobius.gameserver.model.quest.Quest;
import org.l2jmobius.gameserver.model.quest.QuestState;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
public class Q70001_ElmorionCh1 extends Quest
{
// Привяжи к нужным NPC/мобам/предметам
private static final int START_NPC = 30151; // пример: Чад (склад)
private static final int END_NPC = 30152; // пример: Джулия
// private static final int MOB = 20003; // если нужно
- public Q70001_ElmorionCh1()
- {
- super(70001); // ID квеста
- addStartNpc(START_NPC); // кто начинает
- addTalkId(START_NPC, END_NPC);// с кем говорим
- // addKillId(MOB); // если нужно
- // registerQuestItems(...); // если есть предметы квеста
- // addCondMinLevel(1, "70001-lvl.htm"); // пример условия
- }
- @Override
- public String onTalk(Npc npc, PlayerInstance player)
- {
- final QuestState qs = getQuestState(player, true);
- final int npcId = npc.getId();
- if (npcId == START_NPC)
- {
- if (qs.isCreated())
- {
- // Старт
- return "70001-01.htm";
- }
- else if (qs.isStarted())
- {
- // В процессе
- return "70001-02.htm";
- }
- else if (qs.isCompleted())
- {
- // Повторно
- return "70001-complete.htm";
- }
- }
- else if (npcId == END_NPC)
- {
- // Завершение
- // giveItems(player, 57, 1000); addExpAndSp(player, ...);
- // qs.exitQuest(false, true);
- return "70001-end.htm";
- }
- return null;
- }
Скопировать код
b]HTML для квеста кладём обычно в data/html/... (или в твои папки elmlight/elmdark/elmchaos, как вы решили).
Кнопка запуска в диалоге:
[code=html]
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest Q70001_ElmorionCh1">Начать квест</Button>
[/code]
Если используешь «события» (event), верни из onAdvEvent(...) имена html ("70001-01.htm" и т. п.) и в кнопке передавай это событие:
[code=html]
action="bypass -h Quest Q70001_ElmorionCh1 start"
[/code]
2) Зарегистрируй класс в QuestMasterHandler.java
В твоём файле нет метода register() — здесь используется статический массив QUESTS и простое создание экземпляров. Делается так:
а) Добавь import (вверху, среди уже существующих):
[code=java]
import quests.Q70001_ElmorionCh1.Q70001_ElmorionCh1;
[/code]
б) Добавь класс в массив QUESTS (найди массив private static final Class<?>[] QUESTS = { ... } и допиши новую строку):
[code=java]
Q70001_ElmorionCh1.class,
[/code]
И всё — лоадер пройдётся по массиву и вызовет пустой конструктор твоего квеста:
[code=java]
for (Class<?> quest : QUESTS)
{
quest.getDeclaredConstructor().newInstance();
}
[/code]
Чек-лист по регистрации:
[] Есть правильный пакет package quests.Q70001_ElmorionCh1;
[] Импорт добавлен в QuestMasterHandler.java
[] Класс добавлен в массив QUESTS
[] В конструкторе квеста вызывается super(70001)
[] Повешены нужные addStartNpc(...), addTalkId(...), addKillId(...)
[] HTML лежит по путям, куда ссылается код ("70001-01.htm" и т. п.)
3) Пропиши данные для клиента: data/NewQuestData.xml
Этот файл у тебя есть в архиве, его читает сервер и отправляет клиенту корректные сведения о квесте (название, тип, кто старт/финиш, цели, награды для UI).
Структура (реальные поля из твоего файла):
[code=xml]
<quest id="10001" type="1" name="Sedrick's Best Pupil" startNpcId="30008" endNpcId="30008">
<locations>
<param name="startLocationId">485</param>
<param name="endLocationId">485</param>
<param name="questLocationId">485</param>
</locations>
<conditions>
<param name="minLevel">1</param>
<param name="maxLevel">5</param>
<param name="classIds">0;1;4;7;2;3;5;6;8;9</param>
<param name="preQuestId">10000</param>
<param name="oneOfPreQuests">10002;10003</param>
</conditions>
<rewards>
<items>
<item id="91912" count="100"/>
</items>
<param name="rewardExp">12345</param>
<param name="rewardSp">2</param>
<param name="rewardLevel">5</param>
</rewards>
<goals>
<param name="goalItemId">98464</param>
<param name="goalCount">3</param>
<param name="goalString">Herb Roots</param>
</goals>
</quest>
[/code]
Атрибуты у <quest ...>:
[] id — ID квеста (у тебя внутрянка — 70001 и т. д.)
[] type — тип. В твоём файле встречаются 1 и 4:
[] 1 — стандартный (старт от NPC/ленточные обучающие и пр.)
[] 4 — «Request»/охотничье поручение (встречается с startItemId)
[] name — отображаемое имя квеста (английский/локаль клиента)
[] startNpcId / endNpcId — кто стартует/завершает- startItemId — если квест стартует предметом, а не NPC
Внутренние блоки:
[] <locations>: startLocationId, endLocationId, questLocationId — ID локаций для UI-подсказок
[] <conditions>: minLevel, maxLevel, classIds (через ;), preQuestId, oneOfPreQuests
[] <rewards>: блок items (список <item id="" count=""/>) + параметры rewardExp, rewardSp, rewardLevel
[] <goals>: goalItemId, goalCount, goalString (подпись в UI «что делать»)
Пример для твоего квеста 70001:
[code=xml]
<quest id="70001" type="1" name="Elmorion: Chapter I" startNpcId="30151" endNpcId="30152">
<locations>
<param name="startLocationId">561</param>
<param name="endLocationId">562</param>
<param name="questLocationId">562</param>
</locations>
<conditions>
<param name="minLevel">1</param>
<param name="classIds">0;1;2;3;4;5;6;7;8;9</param>
</conditions>
<rewards>
<items>
<item id="57" count="1000"/>
</items>
<param name="rewardExp">500</param>
<param name="rewardSp">1</param>
</rewards>
<goals>
<param name="goalCount">1</param>
<param name="goalString">Talk to Julia</param>
</goals>
</quest>
[/code]
Где вставлять? Внутрь корневого:
[code=xml]
<?xml version="1.0" encoding="UTF-8"?>
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/NewQuestData.xsd">
... тут идут <quest ...> ... </quest> ...
</list>
[/code]
4) «Жёлтый вопросик», портреты и тексты клиента
Вашей сборке они используются на стороне клиента, ориентируйся на такие схемы (типовые структуры из Essence-веток; имена/поля в конкретной ревизии могут отличаться):
4.1 QuestMarkConditionData.xml — правила появления «?» над NPC.
[code=xml]
<?xml version="1.0" encoding="UTF-8"?> <list> <mark questId="70001" npcId="30151"> <conditions> <param name="minLevel">1</param> <param name="maxLevel">40</param> <!-- classIds / preQuestId / oneOfPreQuests и т. п. --> </conditions> </mark> </list> [/code]
4.2 NewQuestDialog.xml — тексты для клиентских окон квеста (заголовки/описания шагов).
[code=xml]
<?xml version="1.0" encoding="UTF-8"?> <list> <dialog questId="70001"> <title>Elmorion: Chapter I</title> <step id="1"> <text>Поговорите с Джулией в эльфийской деревне.</text> </step> <step id="2"> <text>Вернитесь к Чаду, чтобы завершить задание.</text> </step> </dialog> </list> [/code]
4.3 NewQuestNpcPortrait.dat— портреты NPC, показываемые в UI квеста. (клиент)
[code=xml]
<?xml version="1.0" encoding="UTF-8"?> <list> <portrait npcId="30151" icon="Icon.npc_chad" /> <portrait npcId="30152" icon="Icon.npc_julia" /> </list> [/code]
Важно: В ряде ревизий клиент читает эти данные не из *.xml, а из *.dat (NewQuestData-eu.dat, QuestMarkConditionData.dat, NewQuestDialog-eu.dat, NewQuestNpcPortrait.dat). Тогда правка делается в клиенте (и/или через ваши редакторы), а серверный NewQuestData.xml остаётся как источник данных, которые он шлёт клиенту для согласованности UI.
5) Тестирование и типичные ошибки
[] Квест «не виден» вообще: проверь import и наличие Q70001_ElmorionCh1.class в массиве QUESTS.
[] Кнопки «молчат»: проверь bypass -h Quest Q70001_ElmorionCh1 ... и соответствие имён html, которые возвращаешь из onTalk/onAdvEvent.
[] UI пишет Unknown quest: допиши блок в NewQuestData.xml с корректным id, type, name, startNpcId/endNpcId.
[] «Жёлтый ?» не появляется: это клиентская логика — правится QuestMarkConditionData.(xml/dat) (в твоём серверном архиве файла нет).
[] Портрет/текст не показываются: проверь клиентские NewQuestNpcPortrait / NewQuestDialog (в твоём серверном архиве их тоже нет).
[] Исключение при загрузке: смотри лог — обычно ругается на отсутствие пустого конструктора или NPE в конструкторе квеста.
|
|