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

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

Автор: admin    Время: 2024-9-17 19:36
Название: Создаём собственные квесты (часть 2)
Хроники        Interlude
- A* X3 D4 S# L( L4 ]8 T& ^Сборка        L2j7 @( V2 m4 z4 C$ R8 v
Обратите внимание: в файле htm стартового NCP обязательно должна быть ссылка на квест:
' R7 F! B* T! _, l2 c' R
  1. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Для этого квеста больше ничего не надо. Все необходимое для корректной работы квеста уже добавили. Вот полный текст квеста:; i9 A& ]9 F9 i& g7 |3 |6 `1 L! R# J

% G9 K( l3 J' T( v
  1. if getCount(st) >= 4 :  [" w" m% T. v1 `4 t
  2. completed(st)
    ( h" j/ V4 u0 c4 r) Y* q
  3.   return; h; X. v3 k) }3 m% z, b' g
  4. 3 K* ^- x/ R. l+ `2 _
  5. class Quest (JQuest):
    5 N& _( e# W" h, ^3 Z7 x0 Q
  6.   def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr): C% z& W7 s- G& O: M1 U1 K
  7.   def onEvent (self,event,st):# R9 r- |& r8 k1 e( c9 ~+ P/ T
  8. id = st.getState()
      B' ^! ?& Z, D7 _- [
  9. if   id == CREATED  : st.setState(STARTED)" P. m2 Y; A6 C/ m
  10. elif id == COMPLETED: pass
    ' u. W9 ^. a( x- x$ z' ]- t. F
  11. elif id == STARTED  : check(st)
    8 a+ V) L( l4 y  s" w7 N
  12. return
    4 N: C7 d; ]; y+ y1 J% z* i
  13. 8 ^2 b8 E2 _3 C" K5 h
  14. QUEST     = Quest(201,"Tutorial", "Tutorial quest")
    + v2 |: m- d5 _9 F
  15. CREATED   = State('Start',     QUEST)
    ( C. b7 z+ h. O6 t3 A1 M. u: |, B2 r
  16. STARTED   = State('Started',   QUEST)& B" ~# P5 s  h7 I0 ~2 N) _
  17. COMPLETED = State('Completed', QUEST)
    ( h) D5 n; H1 Y0 h
  18. ! X3 t/ D# k: g/ d4 n1 ^
  19. QUEST.setInitialState(CREATED)7 [6 N3 I2 E2 W7 p
  20. QUEST.addStartNpc(7056)1 |' o7 U. E3 E0 o% z. w8 o+ `
  21. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Теперь рассмотрим, как это работает./ @; G( P& r% e' k# o* t
Игрок подходит к начальному NCP (в данном случае 7056), нажимает на «Quest». Квест будет создан и состояние квеста перейдет к CREATED и игроку будет показана страничка Start.htm с описанием квеста. Тогда метод onEvent, поле открытия странички Start.htm переведет состояние квеста в STARTED и игроку будет показана страничка Started.htm, где будет опсание того, как найти keltirs и .т.д.
% \7 R. R$ t1 E! o  {* p3 n
" l1 t* G* a3 K6 k6 i! Y+ j( B
0 v9 F) ^4 i  L
При состоянии STARTED будет зарегистрирован дроп «fangs» при убийстве keltirs. Игрок может вернуться к стартовому NCP и спросить о квесте – метод onEvent будет вызван снова. Если у игрока не хватает необходимого количества предметов, то метод check() не переведет квест в следующее состояние и Started.htm будет показана снова. Но если игрок собрал необходимее количество предметов (в данном случае 4 клыка), то метод check() вызовет метод completed() который переведет квест в новое состояние COMPLETED, заберет все клыки, даст карту мира, т.к. это награда за квест, покажет Completed.htm и завершит квест.  v8 D) `( ?/ Z, l* t. `

+ S7 C7 Q6 q% c. u4 y

( R5 Q/ Y* ^3 [) i. j; MТеперь давайте сделаем наш квест более похожим на то, что он должен из себя представлять.
" ^1 k, P$ R: uПрежде всего у нас есть 3 метода для объявления их в Яве – onTalk, onKill и onEvent. Если методы onTalk и onKill не объявлены, то за них все будет делать метод onEvent, т.е. определять квестовых монстров и вызывать диалоги NCP. Есть примечание, методы onTalk и onKill будут вызывать только диалоги с NCP в зависимости от текущего состояния квеста. Метод onKill будет вызываться только тогда, когда мы убиваем квестового монстра.
4 c: P# w, V* R5 U9 H- U% ^$ I5 g# x; I. o, r: x, k7 f- C
7 T7 A4 g8 C) J9 S+ Y$ Y" [
Давайте рассмотрим как вызывается метод onKill при убийстве keltir в состоянии квеста STARTED:% [3 g. r. n2 m5 \
  1. STARTED.addKillId(KELTIR_NPC_ID)
Скопировать код
и метод onKill в классе Quest:
* c) Z0 }. e( Z0 B% v$ }5 A6 O6 X4 `) V3 V: K
  1. class Quest (JQuest):
      m: [0 u) o& z' o, s! T6 \) \3 m$ t
  2.   ...
    # ~% T3 P! ~/ Z3 R# Q( z. ]- o
  3.   def onKill (self,npcId,st):7 P: W) w6 p# M$ t. ^
  4. if npcId == KELTIR_NPC_ID:
    ( }) w; q! B+ k( W( h9 j: p6 K; O
  5.    n = getCount(st)$ l# |$ K9 d; {+ h9 N
  6.    if   n == 0:, G3 c7 b- V8 o1 j' U
  7.        return "Chat0.htm"7 V) W$ X: s( ~
  8.    elif n == 1:6 c; e" g; @* m. w* p
  9.        return "Chat1.htm"
    7 M) O+ U$ U' {* h6 d9 O$ C
  10.    elif n >= 4:
    - F) g$ X/ O2 W- B  A- k
  11.        return "Chat4.htm"8 j) \; e4 a! f6 B: e% ~5 f" c
  12.    return "Collected "+str(n)+" of 4 fangs"+ {/ Y" A4 E; W$ l1 {# l3 l' G
  13. return
Скопировать код
Метод onKill (а так же метод onTalk) имеет следующие параметры:0 M0 O' h& U- L$ u. l

5 I& h0 k3 D% W5 R! W6 n6 K
& _0 G" e/ d$ C3 n2 I
• self – квест% h9 S* ]/ \$ o% W: D8 K; A: Z2 q
• npcId – ID NCP, которого мы должны убивать (если это метод onTalk, то ID того NCP, с которым мы должны поговорить).
& |0 y6 ~5 B% T• st – текущее состояние игрока.+ D/ {5 }; B+ w; ]
$ ^* B0 W$ N* C" }! i0 W
; S$ _  @4 c% W/ ]
В этом методе мы проверяем и отмечаем, является ли убитый NCP keltir’ом. В основном эта проверка не нужна, т.к. у нас только KELTIR_NPC_ID.) C& t3 ^) O* ]! U' M4 ]5 O7 L
" s5 {0 X% J1 i, Y! [. K
  I! {3 w9 H: Y& h! j6 M& L8 p3 w1 E
Затем проверяем количество предметов (в данном случае количество клыков), и если их вообще нет, то возвращаем строку "Chat0.htm", если только один предмет, то возвращаем строку "Chat1.htm", если же предметов 4 или больше, то "Chat4.htm". Если строка возвращена из методов onEvent, onKill или onTalk, то сервер покажет соответствующие htm. В Chat0.htm может иметь следующий текст: «Вы не имеете ни одного клыка, возвращайтесь позже, когда соберете 4 штуки и бла, бла, бла…», в Chat1.htm может быть следующий текст: «У Вас всего 1 клык, по этому соберите еще…». В Chat4.htm – «Вы собрали необходимое количество предметов, возвращайтесь к вашему тренеру, что бы завершить квест…»
( E3 ~) m3 u- o' |2 i/ W% I- V# S, Q" o, O8 \/ l8 K

. C* j: B) {9 H6 X5 MПримечание: если в строка return начинается с "<html>", то будет показана страничка html с текстом, который стоит далее. Так вместо:2 T% c: j. E  u7 @  S. Z) S
return "Chat4.htm"( i1 b- L7 S/ @$ {5 L$ {

/ U& Z0 F" B& Z2 W; }6 p) X% L
4 H- G* ]) Z4 e
можно поставить:
9 h% x8 A) d  {0 v8 v. Z
  1. return "<html><body>Return to your trainer to complete the quest</body></html>"
Скопировать код
Так же если строка заканчивается без .htm или в начале нет <html>, то текст будет выведен, как системное сообщение в окне чата. В нашем случае мы сделаем так, что бы при каждом убийстве keltik выводилось системное сообщение: «Собрано N из 4-х клыков».
( X, y& o" M  O% r2 l- }3 Q7 X, i7 K' i+ X! w) K+ I! O

# T& A( F+ C+ b5 I& i0 a8 rНаш код для onKill имеет один недостаток. Он будет постоянно показывать Chat0.htm, Chat1.htm и Chat4.htm, нам же необходимо, что бы Chat0.htm и Chat1.htm показывались только один раз. Как нам это сделать? С помощью переменных.( O) [5 o& i) \( d
6 t% D6 a" p  h/ k
/ ^) j; @9 X4 `" h; B
В каждом квесте строки могут храниться с помощью переменных. Эти переменные сохраняются в Вашей БД. В каждом методе мы можем назначить, прочитать и удалить переменные. Давайте изменим метод onKill, так что бы каждый диалог вызывался только один раз.
# ]- w/ C4 }  E4 Q2 ~. L5 B; f
  1. def onKill (self,npcId,st):
    ' e& U) @6 d) f4 g" g
  2. if npcId == KELTIR_NPC_ID:. o# ^3 `: F! ^+ d
  3. n = getCount(st): x/ k' p( s: B  ~8 o; H
  4. if n == 0:
    & }$ p, D& }, W# e% j- a
  5. if st.get('chat0') == None :
    ( p# \2 \8 T. Y+ D
  6. st.set("chat0", "true")
    . Y4 N6 n( g$ e! }* x! I
  7. return "Chat0.htm"
    % F2 g! r6 n7 a6 o0 [
  8. elif n == 1:  `1 e8 r; w7 X6 \+ L" ?( b
  9. if st.get('chat1') == None :9 t! i1 p8 y0 K6 ~/ `! m' Q+ b
  10. st.set("chat1", "true")
    & D; o, b1 }7 ?( ?% ^
  11. return "Chat1.htm"
    : w7 Z! c, x1 c; L) ^
  12. elif n >= 4:) f5 e7 l2 I/ x! _9 J8 C) d
  13. return "Chat4.htm"- g* \, ~$ I1 d& R8 U* Z3 a2 k
  14. return "Collected "+str(n)+" of 4 fangs"
    + g- \% b0 u$ S1 }" x1 j
  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.% ^2 b, {- ]* m. l1 P6 R/ a

+ x9 f$ L; L, @0 |% U6 m5 J: A
3 d6 T6 K( x, Y2 w# i( h) x
Вот конечный рабочий вариант квеста:/ u8 D+ x# z) a# o* i
  1. import sys
    " j1 T: S0 N8 [& P. g* A
  2. from net.sf.l2j.gameserver.model.quest import State
    : J9 ~& |$ |, u7 Q7 t; O
  3. from net.sf.l2j.gameserver.model.quest import QuestState
    $ V8 e. n. e2 P7 r' V" @3 ]
  4. from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest
    3 v+ J, V& l" H* R( _% o$ i

  5. : d, w: E) o% i; C8 g& n# T* o" S
  6. KELTIR_NPC_ID = 120828 d% B9 G& {) z9 b' F
  7. FANGS_ITEM_ID = 1859, n7 r( f# z; w) f# J" o
  8. DROP_RATE    = 500000
    # b3 F2 {" w. O- u1 _0 X7 w2 n

  9. 7 r( c/ }* C4 h' ^+ T
  10. WORLD_MAP_ITEM_ID = 16650 G0 |4 g* t, I% X0 b0 p3 T

  11. : n* I' x, \5 p8 {4 _5 O) E% B
  12. def getCount(st) :
    ! i* @& u7 b, d6 R( B" Q+ ^
  13. return st.getQuestItemsCount(FANGS_ITEM_ID)
    * f6 Q* y+ @. z

  14. & Z* Q% ]" B* h7 I* s( |
  15. def completed(st) :) y) h* A4 W3 A  m( z% I* h; w, a, I
  16. st.setState(COMPLETED)& ~" v/ }! g: q3 _% ]+ y
  17. st.clearQuestDrops()
    : _* I9 ~+ z6 o) G3 U
  18. st.takeItems(FANGS_ITEM_ID,-1)- G: F7 ?- I0 z$ H
  19. st.giveItems(WORLD_MAP_ITEM_ID,1)1 @6 @, |' J- ^& E; `% H
  20. st.exitQuest(False)( O6 }! G3 A' H3 a
  21. return
    0 [- L8 ?8 h% z; `3 \0 ?; R( J+ e

  22. ( I0 I3 N* P1 Q: L  G9 Z1 K
  23. def check(st) :
      e5 L% {+ G, b' c* B- k4 C" P& R
  24. if getCount(st) >= 4 :
    / b- b* \5 t1 A& h
  25. completed(st)3 Q. K7 I, N3 Z! w! F
  26. return
    3 t) r- a- b# r2 I
  27. , r$ L* Z! u2 }. H6 }
  28. class Quest (JQuest):* h9 H- d- o" T% t, G4 f

  29. " P3 ^5 t1 z/ l
  30. def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)) @+ \+ c5 x/ U5 S9 N9 b8 e
  31. & A# r: r- g6 n- @; s2 v
  32. def onEvent (self,event,st):+ x: `- g, P- z
  33. id = st.getState()8 Z, ^' r* W0 v8 N
  34. if id == CREATED : st.setState(STARTED)
    - m! f3 a  o! u9 i0 W
  35. elif id == COMPLETED: pass
    ) M/ y1 K" C) ?1 X  y
  36. elif id == STARTED : check(st)
    " e5 ^; j, l; q
  37. return5 I; p& ~. w* t! P* a
  38. % M* c" D& k9 l8 J5 m! a
  39. def onKill (self,npcId,st):
    3 Q" v7 R8 G, F% v! m
  40. if npcId == KELTIR_NPC_ID:7 z" e# o0 ~' n* `4 x
  41. n = getCount(st)! d: Y' ~5 {9 ^6 U% q0 e2 H/ D
  42. if n == 0:
    / i* |, L. W( J3 i0 f
  43. if st.get('chat0') == None :
    ; s+ Y. p5 c% N
  44. st.set("chat0", "true")) M% f' v' o7 c; k, n1 P( w
  45. return "Chat0.htm": h2 l# @: d/ \( ^! Y
  46. elif n == 1:9 D4 N0 W- z% D; R
  47. if st.get('chat1') == None :) P* Y& \0 P" F3 G) ^
  48. st.set("chat1", "true")
    * O8 `; h* b  E" `3 l
  49. return "Chat1.htm"
    ) \7 ]5 O. i' `/ j8 Y
  50. elif n >= 4:. R/ W3 i  ^3 ]
  51. return "Chat4.htm"( d- I" z4 k) D; k% {  [7 V0 q
  52. return "Collect "+str(n)+" of 4 fangs": M$ H/ G' P2 L- H. a" n! _
  53. return/ ]+ p- i% B. |* d( [3 H9 D+ X
  54. ' @" Y& [: Y) z9 U1 R1 `, L/ ?) G
  55. QUEST    = Quest(201, "Tutorial", "Tutorial quest")
    , s% {  k4 ]$ ?) u4 C
  56. CREATED = State('Start',    QUEST)
    % Z- w' ~, _6 w) d: D
  57. STARTED = State('Started', QUEST)
    ) |4 y8 I' J) l
  58. COMPLETED = State('Completed', QUEST)
      C4 i! \( s' c* g+ D

  59. ' \- T' q$ x6 S3 d( u- {
  60. QUEST.setInitialState(CREATED)
    : J1 |7 ?; y6 e6 @9 f# A: X
  61. QUEST.addStartNpc(7056)
    ' c$ s$ l1 b0 A9 U! q7 P3 R9 i
  62. QUEST.addStartNpc(7012)# n( F' |2 ?) |+ t: {" y! M: n2 p
  63. QUEST.addStartNpc(7009)
    7 Q/ o. q& v4 _. ^2 |
  64. QUEST.addStartNpc(7011)
    0 G: v. @3 O( y" b9 u7 y- P

  65. 3 h: D0 @, L% Z: E* k
  66. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
    . l% z; F* c# U; d% `  t, ]( K
  67. STARTED.addKillId(KELTIR_NPC_ID)# l  o- h$ P' {) }2 R% _
  68. STARTED.addTalkId(7056)* F! _1 ^$ \  \4 N
  69. STARTED.addTalkI d(7012)
    & N/ i; q* V, D$ ]. p
  70. STARTED.addTalkId(7009)8 |7 u6 M; k) b4 d& ~, Q# i
  71. STARTED.addTalkId(7011)
Скопировать код

% W" E5 P0 r; h) D; p
# I# V: F# C" n6 ?. V




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