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

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

Автор: admin    Время: 2024-9-17 19:36
Название: Создаём собственные квесты (часть 2)
Хроники        Interlude
0 N3 [  v, c* b. oСборка        L2j1 M# g9 ?% D% o; C0 o$ T; t+ @
Обратите внимание: в файле htm стартового NCP обязательно должна быть ссылка на квест:" d* T  G6 n  [! ^, S) |, S
  1. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Для этого квеста больше ничего не надо. Все необходимое для корректной работы квеста уже добавили. Вот полный текст квеста:
# z0 @+ r5 R5 `- Z! Z
+ S6 D# U3 l+ x% N+ v. z
  1. if getCount(st) >= 4 :
    ' P! |9 `+ U4 I
  2. completed(st)& G2 a1 ^. v& q; L$ X$ r/ w3 f, Q) e7 U
  3.   return( D; w/ F- |1 V# J, X9 Y

  4. , O2 |9 p2 P' l; {1 z7 N0 T' H
  5. class Quest (JQuest):
    ; f1 u' `0 l- c' D
  6.   def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)9 J6 o) Z  e6 f2 j
  7.   def onEvent (self,event,st):
    1 e2 u6 ^% U, b! x5 y2 A* z# Q7 L5 d/ y
  8. id = st.getState()
    . C; [6 b% Q$ K) V) ?! A
  9. if   id == CREATED  : st.setState(STARTED)( c9 Q0 ]! M/ X- A- d
  10. elif id == COMPLETED: pass: j* x0 b4 v5 S6 G; L$ m
  11. elif id == STARTED  : check(st)" o4 Y4 ?2 O2 [3 j, ]0 H
  12. return
    $ T2 {6 |( w( Q+ L1 `  C9 z* o
  13. ! y) q: k& I* a# B$ x; P
  14. QUEST     = Quest(201,"Tutorial", "Tutorial quest")3 D* R3 w  g; r+ \, Q) Z* g
  15. CREATED   = State('Start',     QUEST)
    & p/ O; _6 j& N" G2 X
  16. STARTED   = State('Started',   QUEST)1 A; e1 k7 q% w& ^1 C4 {7 r6 _5 g
  17. COMPLETED = State('Completed', QUEST)
    : v9 N& d. t( r. Q9 k0 E$ ]* x
  18. 5 \$ ?! |# k+ }) U. J0 S) d
  19. QUEST.setInitialState(CREATED)
    8 a6 K2 a4 ~# S( ~
  20. QUEST.addStartNpc(7056)) a  d* o9 i" k. N
  21. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Теперь рассмотрим, как это работает.4 z* W0 I# U: v; T& h' ~& ~1 k; @
Игрок подходит к начальному NCP (в данном случае 7056), нажимает на «Quest». Квест будет создан и состояние квеста перейдет к CREATED и игроку будет показана страничка Start.htm с описанием квеста. Тогда метод onEvent, поле открытия странички Start.htm переведет состояние квеста в STARTED и игроку будет показана страничка Started.htm, где будет опсание того, как найти keltirs и .т.д.$ S, N2 g" ?, \- A5 d; t) B# P
; c& v$ S1 [5 d0 o) {7 e& x

. p( a. T% O6 V, A$ z: s4 \При состоянии STARTED будет зарегистрирован дроп «fangs» при убийстве keltirs. Игрок может вернуться к стартовому NCP и спросить о квесте – метод onEvent будет вызван снова. Если у игрока не хватает необходимого количества предметов, то метод check() не переведет квест в следующее состояние и Started.htm будет показана снова. Но если игрок собрал необходимее количество предметов (в данном случае 4 клыка), то метод check() вызовет метод completed() который переведет квест в новое состояние COMPLETED, заберет все клыки, даст карту мира, т.к. это награда за квест, покажет Completed.htm и завершит квест.
+ ^4 L. U) D% y5 g
  j4 |! v0 x1 ]) I- V! m7 {
$ s( y  t  b& a) u7 F
Теперь давайте сделаем наш квест более похожим на то, что он должен из себя представлять.8 j! s6 u% M+ `4 K: ^/ `! o: C
Прежде всего у нас есть 3 метода для объявления их в Яве – onTalk, onKill и onEvent. Если методы onTalk и onKill не объявлены, то за них все будет делать метод onEvent, т.е. определять квестовых монстров и вызывать диалоги NCP. Есть примечание, методы onTalk и onKill будут вызывать только диалоги с NCP в зависимости от текущего состояния квеста. Метод onKill будет вызываться только тогда, когда мы убиваем квестового монстра.
/ B6 e# k( o1 a! }, H, ~; M" G
: D& }5 D; S: C! U+ M0 C

8 u* V& {  l# Q3 @Давайте рассмотрим как вызывается метод onKill при убийстве keltir в состоянии квеста STARTED:# p) T! p" b4 M, N2 V
  1. STARTED.addKillId(KELTIR_NPC_ID)
Скопировать код
и метод onKill в классе Quest:
" @" D2 T: [5 W6 z4 {
* o- ?. t/ Y& M+ `
  1. class Quest (JQuest):* H& U8 Z. {, e6 K1 b' h
  2.   ...
    4 v+ j3 @# }! X- k% w7 A3 v
  3.   def onKill (self,npcId,st):" M5 s- z" M- o) ~/ w0 w* C
  4. if npcId == KELTIR_NPC_ID:/ i3 G+ N1 A1 T3 j" t% H0 l
  5.    n = getCount(st)
    0 h$ a9 Q8 g+ X/ `3 J
  6.    if   n == 0:
    & Q" N0 t% p& ]) a" j" ~0 _+ I
  7.        return "Chat0.htm"
    & b" k6 I' S# {! S5 e7 j
  8.    elif n == 1:
    1 T% z; l$ l% n6 y% L
  9.        return "Chat1.htm"/ Y+ O  ^1 m+ v( T: ~
  10.    elif n >= 4:
      c3 O6 E' P* u. L
  11.        return "Chat4.htm"* ]8 Q, q; W, ^
  12.    return "Collected "+str(n)+" of 4 fangs": z; A! @9 o$ i3 F! l5 p: y2 c2 q
  13. return
Скопировать код
Метод onKill (а так же метод onTalk) имеет следующие параметры:
  P( h+ s( p8 W- V7 \  G9 ?7 Y! Q2 D0 V( E
) m8 G4 ?( C. z
• self – квест
8 H' ^: R; w* a! j  u- ~4 D• npcId – ID NCP, которого мы должны убивать (если это метод onTalk, то ID того NCP, с которым мы должны поговорить).
8 O9 C3 i$ `& a3 m2 u• st – текущее состояние игрока.
! {8 k! |( G% F4 X% v/ ~6 }- X' ~5 e+ G* o8 B

* S9 C1 A  f- `) X' M/ pВ этом методе мы проверяем и отмечаем, является ли убитый NCP keltir’ом. В основном эта проверка не нужна, т.к. у нас только KELTIR_NPC_ID.
! J  b) a4 w5 F( K  S4 D, y# [9 W" l4 \1 B0 {. Y9 K

2 d! V4 v5 f# l; lЗатем проверяем количество предметов (в данном случае количество клыков), и если их вообще нет, то возвращаем строку "Chat0.htm", если только один предмет, то возвращаем строку "Chat1.htm", если же предметов 4 или больше, то "Chat4.htm". Если строка возвращена из методов onEvent, onKill или onTalk, то сервер покажет соответствующие htm. В Chat0.htm может иметь следующий текст: «Вы не имеете ни одного клыка, возвращайтесь позже, когда соберете 4 штуки и бла, бла, бла…», в Chat1.htm может быть следующий текст: «У Вас всего 1 клык, по этому соберите еще…». В Chat4.htm – «Вы собрали необходимое количество предметов, возвращайтесь к вашему тренеру, что бы завершить квест…»
7 e/ }( V8 w/ K5 F$ s0 j
4 B0 g" T' \- I% ?8 L- ^

3 G6 e' i! x; T- @/ ^4 z  z) RПримечание: если в строка return начинается с "<html>", то будет показана страничка html с текстом, который стоит далее. Так вместо:0 z' U2 m( G+ h4 g
return "Chat4.htm": W5 M3 F4 T+ [! O
" e* [; W; j  g' H6 j% \: Z0 n" i7 \
. u5 O5 Y' s- E1 O( j
можно поставить:
/ }* f0 [" ~0 n2 u2 R( C# d7 ?. Q
  1. return "<html><body>Return to your trainer to complete the quest</body></html>"
Скопировать код
Так же если строка заканчивается без .htm или в начале нет <html>, то текст будет выведен, как системное сообщение в окне чата. В нашем случае мы сделаем так, что бы при каждом убийстве keltik выводилось системное сообщение: «Собрано N из 4-х клыков».$ ^6 y. p8 h; v' s  b4 W4 Z# s
) \$ T6 E* M' S: W( |
, W' w3 K8 D" W' K( u
Наш код для onKill имеет один недостаток. Он будет постоянно показывать Chat0.htm, Chat1.htm и Chat4.htm, нам же необходимо, что бы Chat0.htm и Chat1.htm показывались только один раз. Как нам это сделать? С помощью переменных.: S. f3 F! b- t, A1 i
0 c- l3 ~# W% s) h( T8 q! z

$ `( k, @2 l8 u& JВ каждом квесте строки могут храниться с помощью переменных. Эти переменные сохраняются в Вашей БД. В каждом методе мы можем назначить, прочитать и удалить переменные. Давайте изменим метод onKill, так что бы каждый диалог вызывался только один раз.2 U9 r& n7 C$ i& ~
  1. def onKill (self,npcId,st):
    : q6 T4 l& O3 a* W4 K  @3 y# K
  2. if npcId == KELTIR_NPC_ID:- z6 i1 ~( H" L3 E
  3. n = getCount(st)1 Z8 v) i+ D3 b# @6 R
  4. if n == 0:1 o: o6 Y. Q6 {  v) l* x
  5. if st.get('chat0') == None :
    & d0 ]: L  \- a+ f
  6. st.set("chat0", "true")5 ~, j$ y6 P2 D% J$ m8 Z' I
  7. return "Chat0.htm"- O* g6 ?* _4 F5 Q0 J# ^" N$ a  q
  8. elif n == 1:& f6 k# @7 M, q' ?
  9. if st.get('chat1') == None :/ |# G% L9 {1 {6 O* p5 ?1 ^
  10. st.set("chat1", "true")4 q4 M+ F4 f+ q/ U
  11. return "Chat1.htm"2 c* ?4 ?& }" v1 U) L' z3 r6 X
  12. elif n >= 4:: G7 @" G( F/ |& _# L  ^
  13. return "Chat4.htm"
    * ?; Z" e/ \( e5 b7 O. g9 F9 L$ J5 g
  14. return "Collected "+str(n)+" of 4 fangs"
    4 g; }- B- B, ^1 n# v
  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./ T8 C4 l3 k9 d, u* C
& r. d) e' v8 O

, }4 }% K8 r5 ]; ]Вот конечный рабочий вариант квеста:
- `+ K8 o. \' j; g: u4 D
  1. import sys
    6 S: i  x6 C, `6 i! c3 w
  2. from net.sf.l2j.gameserver.model.quest import State
    # k3 G5 A- i- s" O( |  ^( @
  3. from net.sf.l2j.gameserver.model.quest import QuestState
    ' F1 N6 q+ F7 @7 w& e
  4. from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest
    - T) I1 L* Y  ?6 T" A' e: z

  5. . \' m  e8 l7 b6 {$ ~
  6. KELTIR_NPC_ID = 12082
    8 |# H  e% Y; D& e6 ?
  7. FANGS_ITEM_ID = 18593 C6 H/ ^; A7 }) U+ K3 J) E" |7 d
  8. DROP_RATE    = 500000( o# h( ^+ ?2 z: K# j1 F' J

  9. ) ]3 V. j) P. M' Q
  10. WORLD_MAP_ITEM_ID = 1665: u- C& c  L9 Q3 U" B  Z" N
  11. 3 ]8 }- S$ L8 J* Q. f# S# |+ s  l0 j0 I
  12. def getCount(st) :
    0 E" E( ~- R" z* f  m+ N5 o+ o
  13. return st.getQuestItemsCount(FANGS_ITEM_ID)) C6 u% M& d' M6 Y6 I! M( l
  14. / J9 z: H6 w& R+ K5 h( Y+ I; t, n
  15. def completed(st) :
    4 t7 @5 z: L" B  r7 U2 j# v- i
  16. st.setState(COMPLETED)2 ~$ L4 g+ c( O+ h0 f
  17. st.clearQuestDrops()
    * g. M6 K# L# k+ j
  18. st.takeItems(FANGS_ITEM_ID,-1)
    1 h( Z; d. L$ T
  19. st.giveItems(WORLD_MAP_ITEM_ID,1)
    ( a) Y' N/ H% Y; D
  20. st.exitQuest(False)
    + U  ~! G0 ~4 Q/ I" u$ L
  21. return8 g0 T# c, ]3 `' ]
  22. 7 n- R2 H# ]& W5 j; ?$ }/ R
  23. def check(st) :
    ( r. b+ X9 d% ?4 W6 |) _5 p2 r, ?9 ^
  24. if getCount(st) >= 4 :6 c0 L- h) q+ L1 X: g
  25. completed(st)/ X9 A6 j2 S/ A9 R* L
  26. return
    # e2 U6 h7 x  M

  27. 8 V$ ~3 Z* D4 x6 Y1 j
  28. class Quest (JQuest):: t, v: j+ a( t
  29. 1 H3 q2 A. X0 Y# n
  30. def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)% f  L% @6 Q* {( R1 h
  31. - W: g5 R  _: i- k2 E9 ^  u- T
  32. def onEvent (self,event,st):
    2 a& j. z& g4 {# I; Z" }
  33. id = st.getState()' k/ O) k: M7 L& f6 Q
  34. if id == CREATED : st.setState(STARTED)6 y$ I; c8 _8 D
  35. elif id == COMPLETED: pass* r3 i: p& p6 q  V, R. w8 w1 j$ w
  36. elif id == STARTED : check(st)
    2 s/ _' a. q( d2 J' w8 z
  37. return  a# `5 Z: H$ t. K
  38. 6 ^) B8 k4 ~6 \2 c
  39. def onKill (self,npcId,st):
    ) m: ?5 h2 R% f) u  B/ R8 x
  40. if npcId == KELTIR_NPC_ID:; [! T( ^1 y0 g$ q6 G
  41. n = getCount(st)+ H$ s4 ^6 F& V3 P
  42. if n == 0:
    2 D! W% S+ ?' x2 V
  43. if st.get('chat0') == None :. K( x4 E& y$ `2 o; N
  44. st.set("chat0", "true")
    % P) ^. u0 @) L0 `9 ^. _
  45. return "Chat0.htm"! R, U3 B! L0 S4 ?" t
  46. elif n == 1:" e& M& o" @) r
  47. if st.get('chat1') == None :
    5 N5 y$ s3 |" F2 d) h) y
  48. st.set("chat1", "true")5 d+ t: a  Q& w5 y! K6 k% e
  49. return "Chat1.htm"9 U6 l1 X" t9 q8 B. r
  50. elif n >= 4:
    / |& d  G: P) V1 D
  51. return "Chat4.htm"
    " f& e: m; ?7 l8 N0 C$ ]$ u/ G
  52. return "Collect "+str(n)+" of 4 fangs"1 e, T  n/ s* {* s. D$ o
  53. return
    ) K2 f! [( @7 f- m
  54. + D0 F* N  N! c2 y6 ]# l
  55. QUEST    = Quest(201, "Tutorial", "Tutorial quest")
    ! J6 P- D+ o, m" ]9 f+ m" J
  56. CREATED = State('Start',    QUEST)
    " U4 g4 i- D7 R) n9 b2 k
  57. STARTED = State('Started', QUEST)" q: i9 j' N5 |% Z9 E
  58. COMPLETED = State('Completed', QUEST)3 l7 K, r5 K2 y

  59. 0 |& F0 M' N' q2 o! \
  60. QUEST.setInitialState(CREATED)+ f. x& k2 m; ^& `
  61. QUEST.addStartNpc(7056)
    # w* Z% O5 L1 _# V2 w: f
  62. QUEST.addStartNpc(7012)' Z3 Q9 v0 [5 w5 |3 r% y; W
  63. QUEST.addStartNpc(7009)2 m; e1 K* r! y2 M
  64. QUEST.addStartNpc(7011)
    # f% A  @% t, @3 I* _

  65. + P* Q7 L: Q9 {' [; q! o* |  A
  66. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
    8 F1 h+ i# M6 {
  67. STARTED.addKillId(KELTIR_NPC_ID)
    ' J+ i5 J9 P3 b
  68. STARTED.addTalkId(7056)0 N) m1 X- ^+ n# g( e7 D
  69. STARTED.addTalkI d(7012)+ Y8 K3 ?5 ]4 |, u
  70. STARTED.addTalkId(7009)9 @+ H6 T6 T  Z& g3 G# }# ?
  71. STARTED.addTalkId(7011)
Скопировать код

( F% K5 d. M& d/ O# V+ M  `2 M" x' q
( n4 J) z' F- T7 Y! p8 t




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