Так как текст получился огромным приходится делить на несколько частй:
I. ЧАСТЬ
Документация по КВЕСТОПИСАНИЮ. Не блещет оригинальностью и тем более не исключены ошибки, так что не судите строго: Квестовый скрипт определяет завершение или не полное завершение потока событий, происходящих от начала до конца квеста. Он включает в себя, но не ограничивает, начало и взятие квеста, последующий комплекс диалогов, убиство мобов, поиск квестовых предметов, спавн квестовых мобов, завершение квеста и получение награды. Для написания и проверки форматирования скрипта я использую программу Python-2.5.1, место рождения: python-2.5.1.msi . Установка и описание ее работы - это отдельная тема. Для примера составим небольшой квест по условию которого игрок сможет получить статус "Дворянин" взамен на выбитый из мобов квестовый итем и на протяжении всего кода я буду добавлять свои коментарии. Для простоты назовем его как нибудь: "2007_noblesse", таким названием мы убиваем сразу 2 зайца - иными словами 2007 - это будет номер-идентификатор квеста, а 'noblesse' - это название квеста, которое должно быть внесено в патч клиента для нормального отображения информации о квесте в момент его выполнения гамером. Хочу также заметить, что для удобочитаемости квеста и более быстрого разбора кода можно использовать коментарии. Для однострочного коментария используется символ # перед строкой текста. Все что написано в коде после этого символа не воспринимается компилятором как код программы. Также можно использовать многострочный символ коментария тройные кавычки: """ до и после комента """. Но этот коментарий используется только до основного кода квеста. Вначале необходимо определить так сказать скелет будущего квеста, естественно не всегда все секции нашего "скелета" используются при написании "квеста" #~1~ Импорт библиотек #~2~ Секция объявление переменных ##~3~ Начало основного кода #~4~ + Секция onEvent() #~5~ + Секция onTalk() #~6~ + Секция onUseSkill() #~7~ + Секция onAttack() #~8~ + Секция onKill() #~9~ + Секция onDeath() ###~10~ Инициализация квеста #~11~ Регистрация добавления переменных #~12~ + addStartNpc() #~13~ + addTalk() #~14~ + addAttack() #~15~ + addSkillUse() #~16~ + addKill() #~17~ + addQuestDrop()
#~~~~~~~~~~~~~~~~~ 1 ~~~~~~~~~~~~~~~~# Для правильного функционирования скрипта, как минимум 3 класса должны быть импортированы из Java:
Код:
|
from net.sf.l2j.gameserver.model.quest import State from net.sf.l2j.gameserver.model.quest import QuestState from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest """ Это демонстрационный квест, который показывает как можно составить самый простой квест """ class Quest (JQuest) :
def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr) |
Кроме того, библиотека jython-а "sys" обычно импортируется для удобства. Если необходимо, можно импортировать больше классов, чтобы получить доступ к другим компонентам ядра и увеличить возможности скрипта: Квестовые скрипты Jython-а по существу наследуются из классов Java net.sf.l2j.gameserver.model.quest.Quest. Разработчики, свободно владеющие языком Java могут посмотреть исходники этого класса, которые размещены на сайте www.l2jserver.com проекта l2jserver, для более детального изучения функций. Кроме того, кое-что вы сможете прочитать в этой документации. #~~~~~~~~~~~~~~~ 2 ~~~~~~~~~~~~~~~~~~# Часто полезно определять имя квеста в переменной, в начале скрипта (обычно используется "qn").Добавляем переменную названия квеста в секции описания переменных:
Код:
Эту переменную можно использовать в различных проверках на состояние квеста, а также в секции описания инициализации самого квеста в окончании вашего скрипта, для того, что бы инициализировать ваш квест и зарегистрировать его в игровом сервере, в самом низу вашего скрипта, вы должны дописать: #~~~~~~~~~~~~~~~ 10 ~~~~~~~~~~~~~~~~~~#
Код:
| QUEST = Quest(2007,qn,"custom") |
"custom" ("custom" будет выведено в диалоговом окне как ссылка на данный квест) вписываем если наш квест находится в папке /jscript/custom, т.е. в эту папку заносятся квесты которые не являются квестами, зарегистрированными в клиентской части игры в файле questname-e.dat. Исходя из этих соображений в htm стартового NPC необходимо указать конкретную ссылку именно на этот квест:
Код:
|
<a action="bypass -h npc_%objectId%_Quest 2007_noblesse">Nobless Quest</a> |
Ну, а если квест "офф" - значит для него место в папке /jscript/quests и ссылка на квест в htm стартового NPC должна выглядеть как обычно:
Код:
| <a action="bypass -h npc_%objectId%_Quest">Quest</a> |
а строка инициализации Quest в конце основного кода квеста соответственно должна выглядеть вот так:
Код:
| QUEST = Quest(626,qn,"A Dark Twilight") |
Статус (STATE): Статусы используются для слежением за сегментами квестов. Каждый статус имеет свой список квестовых предметов, которые могут быть найдены. По завершении квеста или отказа от него, или при переходе квеста от одного состояния к другому, STATES удаляют избыточные квестовые вещи из инвентаря игрока. Для определения state, можно использовать:
Код:
| STATEVARIABLE = State('StateName', QUEST) |
Для примера:
Код:
| STARTED = State('Started', QUEST) |
Статус CREATED обязателен! Все квесты должны иметь его. Это определяется как:
Код:
| CREATED = State('Start', QUEST) |
Статус COMPLETED обязателен для НЕ-повторяющихся квестов. Это определяется как:
Код:
| COMPLETED = State('Completed', QUEST) |
Другие статусы могу быть созданы произвольно. Обычно используемые статусы "Starting", "Started", "Progress" или статусы как "PartXXXX" (Part1, Part2, и т.д.) В нашем случае код выглядит таким образом:
Код:
QUEST = Quest(2017,qn,"custom") CREATED = State('Start', QUEST) STARTED = State('Started', QUEST) COMPLETED = State('Completed', QUEST) |
#~~~~~~~~~~~~~~~ 12 - 13 ~~~~~~~~~~~~~~~~~~# Так как классы мы уже импортировали, далее необходимо зарегистрировать необходимых NPC и мобов для участия в нашем квесте. Проще говоря когда игрок взаимодействует с NPC - сервер получает уведомление об этом и если NPC не зарегистрирован, то сервер не будет вызывать соответствующие вспомагательные функции для обработки полученного уведомления. Функции, описанные ниже, имеют единственную цель: регистрация NPC для инициализацииции событий. Короче говоря, NPC может быть зарегистрирован в квесте для конкретного события, для того, чтобы NPC ответил, когда событие произойдёт. Функции РЕГИСТРАЦИИ не вызываются автоматически. Они должны быть добавлены внизу (в конце) квестового скрипта. Любой квест начинается с разговора со стртовым NPC, а значит он должен быть зарегистрирован в секции:
Код:
QUEST.addStartNpc(npcId) QUEST.addTalkId(npcId) |
Эта функция регистрирует NPC для события onTalk. Этот NPC считается стартовым NPC. Следовательно игрок, не имеющий начатого квеста, не получит доступа в секцию квеста onTalk. И наоборот, когда игрок разговаривает с NPC, зарегистрированным функцией addStartNpc. А параметр "npcId" содержит ID template для этого NPC. В нашем случае он будет равен 31740:
Код:
QUEST.addStartNpc(31740) QUEST.addTalkId(31740) |
при этом если NPC указан в описании переменных :
Код:
тогда в этом случае код квеста в секции регистрации будет выглядеть следующим образом: #~~~~~~~~~~~~~~~ 12 - 13 ~~~~~~~~~~~~~~~~~~#
Код:
QUEST.addStartNpc(CARADINE) QUEST.addTalkId(CARADINE) |
#~~~~~~~~~~~~~~~ 5 ~~~~~~~~~~~~~~~~~~# И так наш гамер подошел к NPC и начинает вести диалог. Если он нажмет на ссылку в окошке диалога с названием Задание (Quest), сервер получит уведомление о том что некий игрок вызвал функцию onTalk и так как это наш стартовый NPC - естественно, зарегистрировав это действие в таблице `character_quests`, активизирует случай onTalk().
Код:
Параметр "npc" содержит ссылку на идентификатор (ID) NPC c которым взаимодействует игрок. Параметр "player" содержит ссылку на идентификатор игрока, который разговаривает с NPC. Параметр "self" - ссылка на сам квест. В нашем случае код будет выглядеть так:
Код:
def onTalk (self,npc,player): st = player.getQuestState(qn) # Присваиваем переменной st данные активного игрока htmltext = "<html><head><body>I have nothing to say you</body></html>" npcId = npc.getNpcId() # Присваиваем переменной npcId данные выбранного NPC if not st : return htmltext # Проверяем на состояние квеста, действительно ли у нашего игрока данный квест активен cond = st.getInt("cond") # Присваиваем переменной cond данные из таблицы `character_quests` onlyone = st.getInt("onlyone") if npcId == CARADINE: htmltext = "31740-01.htm" return htmltext |
Теперь как видно из кода сервер выдаст игроку htm-страничку, находящуюся в папке нашего квеста. Игрок автоматически получает статус CREATED для этого квеста и эта переменная записывается в талицу `character_quests`. Данные в этой таблице будут выглядеть следующим образом:
Цитата:
------------------------------------------ name | var | value ------------------------------------------ 2007_noblesse | <state> | Start ------------------------------------------ |
|