artace.ru - творческий сектор

Название: Создаём собственные квесты (часть 2) [Версия для печати]

Автор: admin    Время: 2024-9-17 19:36
Название: Создаём собственные квесты (часть 2)
Хроники        Interlude8 F( R! c. `% l2 d: f
Сборка        L2j6 a* E# z" s- B3 X$ D# S$ R& x
Обратите внимание: в файле htm стартового NCP обязательно должна быть ссылка на квест:
2 o5 {( {% s) }. \5 }
  1. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Для этого квеста больше ничего не надо. Все необходимое для корректной работы квеста уже добавили. Вот полный текст квеста:
0 t/ k: w# T) B0 X6 E& s6 L* d& R% i: h: C: Z4 T! A/ A
  1. if getCount(st) >= 4 :6 v8 a: s2 ?2 ^% H9 h2 V3 K* p8 D
  2. completed(st)
    & @2 }! {; o9 ~  g0 ]9 d6 x$ Q
  3.   return
    & q* V; X. r* i5 P

  4. 1 b2 N3 x. S: [* n3 I6 O- q9 K/ c; {
  5. class Quest (JQuest):
    9 r6 o& n. V4 c5 y2 ]; I
  6.   def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)
    . o# f2 `2 `6 S8 v- y) c
  7.   def onEvent (self,event,st):
    ( }' J' E" k; |
  8. id = st.getState()4 j1 [5 Z% c; P0 D* r
  9. if   id == CREATED  : st.setState(STARTED)  q5 e4 b4 Z) R% }) T- R9 W1 I5 i
  10. elif id == COMPLETED: pass+ ^/ Z* T; G! G" i5 u) v0 b
  11. elif id == STARTED  : check(st)9 e- y2 h8 K. v" ~, p& v
  12. return3 f, [: R8 a) |: v3 R3 j
  13. 2 S! W& C% `+ d2 [2 b# u- f
  14. QUEST     = Quest(201,"Tutorial", "Tutorial quest"); K/ J( c2 C) B  @8 S
  15. CREATED   = State('Start',     QUEST)& M( j2 J2 ]; ~" O7 a% X2 u! r
  16. STARTED   = State('Started',   QUEST)
    ; p! A, `2 @& v) }5 ]
  17. COMPLETED = State('Completed', QUEST)
    / i2 T; U7 ^9 l5 j  e

  18. . Q0 y- m# k/ j( e8 Y
  19. QUEST.setInitialState(CREATED)
    : T4 G# k1 F4 x
  20. QUEST.addStartNpc(7056); T4 n4 G* B% ]& f! d: }- r
  21. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Теперь рассмотрим, как это работает.
1 J: p# z6 o5 C+ xИгрок подходит к начальному NCP (в данном случае 7056), нажимает на «Quest». Квест будет создан и состояние квеста перейдет к CREATED и игроку будет показана страничка Start.htm с описанием квеста. Тогда метод onEvent, поле открытия странички Start.htm переведет состояние квеста в STARTED и игроку будет показана страничка Started.htm, где будет опсание того, как найти keltirs и .т.д.
/ G) M4 F+ ~9 ?8 r
2 c8 f0 r' y2 @5 q3 F

& E! w$ O1 |, F6 {При состоянии STARTED будет зарегистрирован дроп «fangs» при убийстве keltirs. Игрок может вернуться к стартовому NCP и спросить о квесте – метод onEvent будет вызван снова. Если у игрока не хватает необходимого количества предметов, то метод check() не переведет квест в следующее состояние и Started.htm будет показана снова. Но если игрок собрал необходимее количество предметов (в данном случае 4 клыка), то метод check() вызовет метод completed() который переведет квест в новое состояние COMPLETED, заберет все клыки, даст карту мира, т.к. это награда за квест, покажет Completed.htm и завершит квест.
! q5 ?6 W7 t% p+ @
2 }8 `& e1 N6 G& `$ \0 I* f# u) R

- g1 f4 o3 K  z/ {- U6 sТеперь давайте сделаем наш квест более похожим на то, что он должен из себя представлять.
; S; f( y8 d+ C( Q6 I: M6 eПрежде всего у нас есть 3 метода для объявления их в Яве – onTalk, onKill и onEvent. Если методы onTalk и onKill не объявлены, то за них все будет делать метод onEvent, т.е. определять квестовых монстров и вызывать диалоги NCP. Есть примечание, методы onTalk и onKill будут вызывать только диалоги с NCP в зависимости от текущего состояния квеста. Метод onKill будет вызываться только тогда, когда мы убиваем квестового монстра.7 x' H9 j& f% ]3 q- L( ~5 i( g+ h5 I8 v
7 R7 \! m0 d& f- x  t

# [! t5 b1 Q; ?1 f, t, {Давайте рассмотрим как вызывается метод onKill при убийстве keltir в состоянии квеста STARTED:% B9 y2 s7 v; L2 V$ y
  1. STARTED.addKillId(KELTIR_NPC_ID)
Скопировать код
и метод onKill в классе Quest:3 d' j# C" Y0 u0 q

: `4 u- E3 ^& O7 o% ~  g
  1. class Quest (JQuest):9 |7 p5 A0 @% b, i3 C3 c
  2.   .../ `3 j2 k% L1 T1 {; Y0 U: ?
  3.   def onKill (self,npcId,st):
    , p0 G6 ~% y% d! w
  4. if npcId == KELTIR_NPC_ID:
    ! \. n  U: k. M7 B7 i
  5.    n = getCount(st)  _3 m+ Y$ Z! [+ ]- e7 c# U: F5 J# u
  6.    if   n == 0:
    : R% x2 z$ V% p' e  X& |
  7.        return "Chat0.htm"
    ( N: b* N* L  u3 I; H5 j0 b) O) A
  8.    elif n == 1:. [# b) i' ~9 F% Z- T
  9.        return "Chat1.htm"
    % w. h6 r4 E: w  R( }8 z; f
  10.    elif n >= 4:. `( ^5 o: K1 t! _3 {0 Z4 t- [
  11.        return "Chat4.htm"
    / \* R8 W( o3 r: G: D$ j
  12.    return "Collected "+str(n)+" of 4 fangs"6 V; b, B. i  [% W% O6 J  S
  13. return
Скопировать код
Метод onKill (а так же метод onTalk) имеет следующие параметры:" t! b) i* [; z. D5 o
2 N% Y) h8 G& e- q3 x

% M$ M) M- ]) ]& l9 x* |" z• self – квест$ {# [/ N3 \. \6 [
• npcId – ID NCP, которого мы должны убивать (если это метод onTalk, то ID того NCP, с которым мы должны поговорить).
, B3 o8 B9 q' `$ i1 X& }; `- ?• st – текущее состояние игрока.
3 Q3 r7 X5 P$ X9 U  b
! I3 o& B4 P: Y7 `
& S) \$ s$ j! ]$ k
В этом методе мы проверяем и отмечаем, является ли убитый NCP keltir’ом. В основном эта проверка не нужна, т.к. у нас только KELTIR_NPC_ID.
* ~2 ~6 ~" ^+ B, C8 L7 F- A9 `: t5 h! x' u5 B
+ h1 S" z! w* i4 m7 M
Затем проверяем количество предметов (в данном случае количество клыков), и если их вообще нет, то возвращаем строку "Chat0.htm", если только один предмет, то возвращаем строку "Chat1.htm", если же предметов 4 или больше, то "Chat4.htm". Если строка возвращена из методов onEvent, onKill или onTalk, то сервер покажет соответствующие htm. В Chat0.htm может иметь следующий текст: «Вы не имеете ни одного клыка, возвращайтесь позже, когда соберете 4 штуки и бла, бла, бла…», в Chat1.htm может быть следующий текст: «У Вас всего 1 клык, по этому соберите еще…». В Chat4.htm – «Вы собрали необходимое количество предметов, возвращайтесь к вашему тренеру, что бы завершить квест…»
2 z0 I7 L1 L  j2 V4 d5 o  l! o
: B9 Z, n+ P$ J4 c& o

5 D9 s: P( s! P$ X2 NПримечание: если в строка return начинается с "<html>", то будет показана страничка html с текстом, который стоит далее. Так вместо:
1 F' d3 r# R7 X0 Q( A& w% e; lreturn "Chat4.htm": P: @, B; i3 c. K8 J

3 w, w  ?+ s! a( |# B
7 g8 e, ]; p$ s* ~6 f2 O2 x
можно поставить:
5 @$ P$ S' f: Q% X
  1. return "<html><body>Return to your trainer to complete the quest</body></html>"
Скопировать код
Так же если строка заканчивается без .htm или в начале нет <html>, то текст будет выведен, как системное сообщение в окне чата. В нашем случае мы сделаем так, что бы при каждом убийстве keltik выводилось системное сообщение: «Собрано N из 4-х клыков».$ ^  f; I+ u0 q9 _+ S

9 ^& m  L( v" Y, y; V
0 H" p" ?) J0 A# W0 e
Наш код для onKill имеет один недостаток. Он будет постоянно показывать Chat0.htm, Chat1.htm и Chat4.htm, нам же необходимо, что бы Chat0.htm и Chat1.htm показывались только один раз. Как нам это сделать? С помощью переменных.! W7 N& m5 ~" I& @
& P8 P+ V4 h, T2 p. ?
2 _4 {2 }, d$ F
В каждом квесте строки могут храниться с помощью переменных. Эти переменные сохраняются в Вашей БД. В каждом методе мы можем назначить, прочитать и удалить переменные. Давайте изменим метод onKill, так что бы каждый диалог вызывался только один раз.* D+ {2 \5 J8 D" t3 q, I
  1. def onKill (self,npcId,st):. \* E8 K" @$ O5 P  I- o
  2. if npcId == KELTIR_NPC_ID:# L% Y9 P. H& y3 o5 x/ m) j
  3. n = getCount(st)5 `* o: X& h; D& S
  4. if n == 0:
    ; Y( E3 g+ s  q& z/ h* V( v) ]
  5. if st.get('chat0') == None :" O; ]( D. J7 q9 z8 o2 K. k) ?
  6. st.set("chat0", "true")% y6 P: O0 U% T: K& @
  7. return "Chat0.htm"! |2 S, {. D" q
  8. elif n == 1:
    ' t6 \  T; q8 q9 \; Q
  9. if st.get('chat1') == None :
    4 j& e+ t" T" w, t) e" [
  10. st.set("chat1", "true")
    9 x& D& A4 R, L# S- q' _
  11. return "Chat1.htm"
    # U5 j) Z: m' e- C; a! M2 R
  12. elif n >= 4:1 Y- S$ j* \8 g) P  H3 e8 n
  13. return "Chat4.htm"
    & W: g* Y8 n; `9 i
  14. return "Collected "+str(n)+" of 4 fangs"* N: @) v8 W% A0 I+ v# ^4 G* p  H
  15. return
Скопировать код
Если у игрока нет клыков (n=0), то мы получаем занчение переменной 'chat0'. Когда метод onKill вызван в первый раз, то пока ни каких переменных не имеется и python возвращает значение None. В этом случае объявляется переменная и показывается диалог Chat0.htm. Когда мы убиваем keltir, но не получаем с него клык, функция st.get('chat0') возвращает строку true, а не None. И во второй раз окно с Chat0.htm не появится, но в окне чата появится строчка «Collected 0 of 4 fangs». По тому же принципу сделано и с Chat1.htm.
6 k7 F, \9 j- J8 q' b: t# J
" Y# n  k! O1 L; Q+ M

' d& ?3 r$ c1 E! s4 N& O$ dВот конечный рабочий вариант квеста:
. r( B1 R* ]' o/ [
  1. import sys
    4 V2 O+ p/ I: _$ ~3 q
  2. from net.sf.l2j.gameserver.model.quest import State
    * ~6 H9 t/ t& l3 H; T5 R& r
  3. from net.sf.l2j.gameserver.model.quest import QuestState9 N- E5 C& M  b& L
  4. from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest( b8 N5 P# L; K2 [: C

  5. * n5 U/ |9 `2 d
  6. KELTIR_NPC_ID = 12082
    , W3 p% {: _+ f  v* `$ G+ E
  7. FANGS_ITEM_ID = 1859
    * c" O! f" H6 D$ e* a7 M, [
  8. DROP_RATE    = 500000$ L1 g) U% r5 K4 }% d' {

  9. . J5 m! E7 l4 Q
  10. WORLD_MAP_ITEM_ID = 1665! R% ?' \) s0 {& m( D# G

  11. & O- z4 {0 g! K' \/ v. w" c1 F% u
  12. def getCount(st) :
    - w- d$ Q: A" }% a& z5 e- M. x% h
  13. return st.getQuestItemsCount(FANGS_ITEM_ID)
    # L. r* l* T" g& ?+ H
  14. & Y8 G3 O5 I- m7 b
  15. def completed(st) :( ?6 Z$ O5 v( G" p' G* N4 D
  16. st.setState(COMPLETED)* ~$ ?# x" X/ ]- u* S+ Q' J5 @
  17. st.clearQuestDrops()
    " S( g3 w& c- R- @7 A" [6 D* F& U
  18. st.takeItems(FANGS_ITEM_ID,-1)
    2 d, f' y( ]3 m8 M1 H3 O
  19. st.giveItems(WORLD_MAP_ITEM_ID,1), A$ i! o9 Z* s1 U% h( x3 F) X
  20. st.exitQuest(False)( O8 |7 I7 d. |: i: d# f
  21. return
    7 K! |6 S  F$ \8 g, p7 e+ b( \3 _6 @
  22. : G( d, p" d% q7 s5 O: t1 f* e
  23. def check(st) :4 f  i, a" [' b8 w! W
  24. if getCount(st) >= 4 :
    3 L( i" _# v* f) L$ v- ]/ j0 g
  25. completed(st)
    4 b4 m: R/ Z5 O) q
  26. return
    : _* t6 N& {' C7 [8 G, i# j7 V
  27. 4 P& z5 r" f5 N! s' a# J  ^& \* b1 A
  28. class Quest (JQuest):+ z2 Q2 H) P$ L9 q, r  n

  29. 5 N/ C4 o4 E8 Y" A
  30. def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)
    9 |4 _; {8 L1 k7 J

  31. ' f5 s; _1 v. w1 s# J
  32. def onEvent (self,event,st):" t3 C  n9 w! u) ?" o
  33. id = st.getState()4 [! o3 {( g* F, Z
  34. if id == CREATED : st.setState(STARTED)) u/ z! V. N, a$ O
  35. elif id == COMPLETED: pass
    " V2 M: B9 {- b, {* Y  k& v
  36. elif id == STARTED : check(st)
    ! u0 k. k+ j  O
  37. return
    * p# ~/ Z" N" I: P  L

  38. 6 k+ _0 A; ]0 W' T
  39. def onKill (self,npcId,st):
    & {7 N+ Z. F$ V9 \
  40. if npcId == KELTIR_NPC_ID:8 S6 ^5 v6 @+ L& r) `8 \8 E$ q
  41. n = getCount(st)
    , B8 F0 W( }/ @! _7 q# R; Q' V( I
  42. if n == 0:" `0 }1 i, o( K: d$ [/ q
  43. if st.get('chat0') == None :' c$ d6 b7 H' A
  44. st.set("chat0", "true")
    + j5 q) N. z7 \
  45. return "Chat0.htm"  d2 y9 L  H- }% c% o
  46. elif n == 1:* _: t; ]( b! N/ V4 O  u( T
  47. if st.get('chat1') == None :
    , H& L# x$ b  ]* G8 w$ {
  48. st.set("chat1", "true")6 `3 u8 |, B0 T6 f
  49. return "Chat1.htm"2 E/ T( ~# o& u- ^" ~  F
  50. elif n >= 4:, Z5 v$ x: \0 z9 O( S
  51. return "Chat4.htm"
    2 U/ |9 ?8 N. v, b
  52. return "Collect "+str(n)+" of 4 fangs"0 y4 ?, S" W1 T% V8 p+ U
  53. return% D7 |2 C4 W. I& c8 ^

  54. , E; ^, [* C0 ~! y3 Z( f
  55. QUEST    = Quest(201, "Tutorial", "Tutorial quest")% E4 s& n) h- ^' r3 s$ ^  {
  56. CREATED = State('Start',    QUEST)
    3 r+ [% J3 D% C) c3 Y
  57. STARTED = State('Started', QUEST)# m5 v! M- ^! g6 X- a; W
  58. COMPLETED = State('Completed', QUEST)) S+ c- U1 K4 t2 Y, j8 U4 V9 y6 ]8 T
  59. # Z& V# Z0 y4 ^
  60. QUEST.setInitialState(CREATED): E! l4 K  \& r9 v" P
  61. QUEST.addStartNpc(7056)
    * }) L, h: H, j0 k' C; K: `( H
  62. QUEST.addStartNpc(7012)
    / y8 n! @( K5 O9 {2 ]' e
  63. QUEST.addStartNpc(7009)9 h- H# ~6 i+ n' M- l$ v6 b
  64. QUEST.addStartNpc(7011)
    ) O# l1 h* A( ?# V  Y- h/ J/ K

  65. 8 A! |; }* C: _3 t1 H# |
  66. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
    / X6 l5 ?: c& Q6 @; Z* N6 @
  67. STARTED.addKillId(KELTIR_NPC_ID)1 w$ H. ]/ p. j, l! i
  68. STARTED.addTalkId(7056); Y! X" e0 W  F0 h% y' ^
  69. STARTED.addTalkI d(7012)
    5 W8 X, r' o# }: \, a3 c
  70. STARTED.addTalkId(7009)# }( w* i4 h: l$ H0 `
  71. STARTED.addTalkId(7011)
Скопировать код

7 c4 v/ n. E) P# W' p, ^
5 t6 d/ g5 c0 D0 s/ ~




Добро пожаловать artace.ru - творческий сектор (https://www.artace.ru/) Powered by Discuz! X3.5