Суббота, 20.04.2024, 05:56
Приветствую Вас Гость | RSS
Главная | Квест | Регистрация | Вход
Меню сайта
Форма входа
Логин:
Пароль:
Календарь
«  Апрель 2024  »
ПнВтСрЧтПтСбВс
1234567
891011121314
15161718192021
22232425262728
2930
Архив записей
Наш опрос
На каком клиенте вы играете?
Всего ответов: 216
Наша кнопка
Статистика
 

Онлайн всего: 1
Гостей: 1
Пользователей: 0
INFO


Зарегистрировано на сайте:

Всего: 42964
Новых за месяц: 0
Новых за неделю: 0
Новых вчера: 0
Новых сегодня: 0
Из них:
Пользователей 42962
Проверенных: 0
Модераторов: 0
Админов: 2
Из них:
Парней: 42962
Девушек: 2
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").Добавляем переменную названия квеста в секции описания переменных:
   Код:
qn = "2007_noblesse"
Эту переменную можно использовать в различных проверках на состояние квеста, а также в секции описания инициализации самого квеста в окончании вашего скрипта, для того, что бы инициализировать ваш квест и зарегистрировать его в игровом сервере, в самом низу вашего скрипта, вы должны дописать:
#~~~~~~~~~~~~~~~ 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 указан в описании переменных :
   Код:
#NPC
CARADINE = 31740
тогда в этом случае код квеста в секции регистрации будет выглядеть следующим образом:
#~~~~~~~~~~~~~~~ 12 - 13 ~~~~~~~~~~~~~~~~~~#
   Код:
QUEST.addStartNpc(CARADINE)
QUEST.addTalkId(CARADINE)
#~~~~~~~~~~~~~~~ 5 ~~~~~~~~~~~~~~~~~~#
И так наш гамер подошел к NPC и начинает вести диалог. Если он нажмет на ссылку в окошке диалога с названием Задание (Quest), сервер получит уведомление о том что некий игрок вызвал функцию onTalk и так как это наш стартовый NPC - естественно, зарегистрировав это действие в таблице `character_quests`, активизирует случай onTalk().
   Код:
onTalk(self,npc, player)
Параметр "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
------------------------------------------
II. ЧАСТЬ

*ПРИМЕЧАНИЕ ДЛЯ ТЕХ КТО В ТАНКЕ И БЕЗ ШЛЕМОФОНА:

Обращаю ваше внимание на то что в этой таблице еще пока нет переменной "cond")
Статуc квеста (QuestState) (состояние квеста):
QuestState – это не часть определения квеста как таковая, но в ней содержится информация, которая отслеживает весь процесс развития событий конкретного игрока в этом квесте. Возьмём пример с игроком, статус квеста игрока в этом квесте можно узнать, используя:
   Код:
st = player.getQuestState("2007_noblesse")
или
   Код:
st = player.getQuestState(qn)
Если игрок не имеет статус квеста(quest-state) для этого квеста (т.е. игрок в данный момент не делает этот квест), тогда «st» будет нулевым.
Кроме того, "queststate" любого члена группы, который имеет конкретную переменную и значение сохранённое для этого квеста, можно узнать с использованием:
   Код:
partyMember = self.getRandomPartyMember(player,"variable","value")
st = partyMember.getQuestState("12345_questname")
Аналогично, "queststate" произвольного члена группы, который достиг конкретного СОСТОЯНИЯ (STATE) для этого квеста, можно узнать с использованием:
   Код:
partyMember = self.getRandomPartyMemberState(player,STATE)
st = partyMember.getQuestState("12345_questname")
Для примера, вместо "variable" и "value" в первом примере, можно использовать "cond" и "1", а Вместо "STATE" во втором примере, можно использовать "STARTED".
#~~~~~~~~~~~~~~~ 4 ~~~~~~~~~~~~~~~~~~#
Если в диалоге вызваном игроком существует ссылка-приглашение продолжить квест или другого действия такого формата:
   Код:
<a action="bypass -h Quest 2007_noblesse 31740-02.htm">Да</a>
где 31740-02.htm - является переменной для события onEvent() :
   Код:
onEvent(self, event, st)
при этом сервер получает уведомление о том что квест '2007_noblesse' вызвал случай onEvent(), в котором мы можем присвоить разным переменным новые значения, которые будут переводить квест в следующее действие.
Параметр "st" содержит ссылку на QuestState (статус квеста) игрока, который использует ссылку.
Параметр "event" содержит строковый идентификатор для события. В основном, эта строка находится прямо в ссылке, но она также может использоваться при установке так называемого таймера, названием которого и будет являться данный строковый идентификатор.
Параметр "self" - ссылка на сам квест. Вы можете использовать self.XXXX, где XXXX – любая функция, объявленная в родительском классе.
в нашем случае мы при помощи данного случая переводим квест в следующее состояние:
   Код:
 def onEvent (self,event,st) :
   htmltext = event                    # Присваиваем переменой htmltext значение строкового идентификатора event
   cond = st.getInt("cond")            # Присваиваем переменой cond данные из таблицы `character_quests`
   if event == "31740-02.htm" :
     if cond == 0 :
       st.set("cond","1")              # Присваиваем переменой cond значение 1 для и записываем ее в таблицу `character_quests`
       st.setState(STARTED)            # Переводим состояние квеста в STARTED
       st.playSound("ItemSound.quest_accept") # Озвучка взятия квеста
   return htmltext
При смене переменной "cond" квест переходит на следующую ступень и в клиентской части игры, точнее в разделе описания квестов будет выдаваться сообщение о том что предыдущая часть квеста пройдена т.е. 'Completed', но в нашем случае описание квеста не внесено в клиентскую часть в файл 'questname-e.dat и поэтому описание выводиться не будет.
Теперь, если обратить внимание на таблицу `character_quests` мы увидим что данные в этой таблице несколько изменились:
   Цитата:
------------------------------------------
name | var | value
------------------------------------------
2007_noblesse | <state> | Started
------------------------------------------
2007_noblesse | cond | 1
------------------------------------------
 **ПРИМЕЧАНИЕ ДЛЯ ТЕХ КТО В ТАНКЕ И БЕЗ ШЛЕМОФОНА:
   Цитата:
Пока QuestState (состояние квеста) у игрока будет обнаруживаться, мы также будем иметь доступ к игроку, если это понадобится, используя:
   Код:
 
   st.getPlayer()
 
Все другие общепринятые методы реализации QuestState доступны из jython. Точно также, объекты, извлекаемые из «st» могут быть в будущем использоваться, для того, чтобы извлечь больше. Для примера, можно сделать приблизительно так:
   Код:
 
   st.getPlayer().getClan().getLeader().getPlayerInstance().getPet()
 
(этот пример как маленькая демонстрация возможностей - как глубоко можно проникнуть в цепь объектов, которые являются доступными. В этом случае, из QuestState, мы получаем игрока, который имеет QuestState, затем получаем клан игрока, затем лидера клана игрока, фактическое состояние лидера и уже там мы находим вызванного лидером пета!)
 #~~~~~~~~~~~~~~~ 2 ~~~~~~~~~~~~~~~~~~#
Теперь задание получено и игрок отправляется мочить какого нибудь монстра из которого должен выпасть квестовый итем к примеру Feastival Adena (Id == 6673), а значит нам необходимо зарегистрировать этот итем в разделе описания переменных и зарегистрировать МОНСТРА из которого этот итем будет выпадать, в случае если этого монстра замочить. В нашем случае мы добавляем строки :
   Код:
#NPC
CARADINE = 31740
# Items
FESTIVAL_ADENA_ID = 6673
# Mobs
KRANROT = 20650
III. ЧАСТЬ
 
#~~~~~~~~~~~~~~~ 17 ~~~~~~~~~~~~~~~~~~#
и конечно же необходимо добавить квестовый итем в секцию регистрации квестового дропа:
   Код:
STARTED.addQuestDrop(npcId,itemId,1)
Параметр "npcId" - содержит ID template зарегистрированного в секции addKillId(npcId)
Параметр "itemId" - выпадающий квестовый итем
Параметр 1 - количество выпадающих итемов
STARTED.addQuestDrop(KRANROT,FESTIVAL_ADENA_ID,1)
#~~~~~~~~~~~~~~~ 16 ~~~~~~~~~~~~~~~~~~#
Регистрация NPC для события onKill.
Конечно же необходимо зарегистрировать все это в секции регистрации квеста :
   Код:
addKillId(npcId)
Параметр "npcId" содержит ID template для этого NPC, но в нашем случае мы зарегистрировали его в разделе обьявления переменых и поэтому код будет выглядеть вот так:
   Код:

#~~~~~~~~~~~~~~~ 12 - 16 ~~~~~~~~~~~~~#
QUEST.setInitialState(CREATED)
QUEST.addStartNpc(CARADINE)

QUEST.addTalkId(CARADINE)

QUEST.addKillId(KRANROT)

Игрок должен быть зарегистрирован на этот квест, только в этом случае сервер получит уведомление об убийстве квестового NPC, который зарегистрирован в данной секции..
#~~~~~~~~~~~~~~~~~~~~ 8 ~~~~~~~~~~~~~~~~~~~~~~#
Итак наступает момент когда наш игрок наконец-то нашел и замочил необходимого (зарегистрированного в секции addKill() монстра, в этом случае сервер получает уведомление об убийстве квестового NPC и вызывает случай onKill() :
   Код:
onKill(self, npc, player)
Параметр "npc" содержит ссылку на идентификатор (ID) NPC который был убит.
Параметр "player" содержит ссылку на идентификатор игрока, который убил.
Параметр "self" работает так же как и в onEvent, т.е. "self" - ссылка на сам квест
Для нашего квеста код будет выглядеть следующим образом:
   Код:
 def onKill (self, npc, player) :
   st = player.getQuestState(qn)
   if not st : return
   if st.getState() != STARTED : return
   npcId = npc.getNpcId()
   cond = st.getInt("cond")
   if npcId == KRANROT :                          # Если NPC действительно соответствует зарегистрированному в секции регистрации addKill()
      if cond == 1 and st.getRandom(100)>70 :      # Проверка состояние квеста ("cond") и на рандомное число
         st.giveItems(FESTIVAL_ADENA_ID,1)         # Если предыдущие проверки возвращают истину - тогда игрок получает квестовый итем
         st.playSound("ItemSound.quest_middle")    # Озвучка окончания этой части квеста
         st.set("cond","2")                        # Перевод состояния квеста на следующую ступень
   return
После этого обратите внимание на таблицу `character_quests` :
   Цитата:
------------------------------------------
name | var | value
------------------------------------------
2007_noblesse | <state> | Started
------------------------------------------
2007_noblesse | cond | 2
------------------------------------------
В случае если игрок уже получивший квестовый итем опять решит завалить еще одного монстра то квестовый итем он уже не получит, так как проверка на состояние переменной "cond" вернет уже ложное состояние (if cond == 1, но в таблице `character_quests` уже стоит 2)
#~~~~~~~~~~~~~~~~~ 5 ~~~~~~~~~~~~~~~~~~~#
Получив необходимый квестовый итем игрок возвращается к NPC который "заказал" ему убийство этого монстра и начинает вести диалог:
   Код:
      elif cond == 2 and st.getQuestItemsCount(FESTIVAL_ADENA_ID) >= 1 : # Случай когда диалог начинает игрок уже выбивший квест.итем
         st.takeItems(FESTIVAL_ADENA_ID,-1)        # Отбираем необходимый для квеста итем
         st.set("cond","0")                        # Обнуляем переменную "cond"
         st.getPlayer().setNoble(True)             # Даем статус Дворянина
         st.giveItems(NOBLESS_TIARA,1)             # Выдаем памятный подарок
         st.playSound("ItemSound.quest_finish")    # Озвучка окончания квеста
         st.setState(COMPLETED)                    # Перевод квеста в состояние COMPLETED
         st.set("onlyone","1")                     # Добавляем переменную "onlyone" для одноразовости квеста.
         htmltext = "31740-04.htm"                 # Поздравления
И в последний раз обращаем внимание на табличку:
   Цитата:
------------------------------------------
name | var | value
------------------------------------------
2007_noblesse | <state> | Completed
------------------------------------------
2007_noblesse | cond | 0
------------------------------------------
2007_noblesse | onlyone | 1
------------------------------------------
На этом наш демо-квест заканчивается.

Copyright MyCorp © 2024