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

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

Автор: admin    Время: 2024-9-17 19:36
Название: Создаём собственные квесты (часть 2)
Хроники        Interlude1 D* M, y. O$ ^: J, {7 z
Сборка        L2j( J+ |. m6 h! o$ Q
Обратите внимание: в файле htm стартового NCP обязательно должна быть ссылка на квест:; `1 [, r5 ^4 i5 k. j
  1. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Для этого квеста больше ничего не надо. Все необходимое для корректной работы квеста уже добавили. Вот полный текст квеста:
" S% i4 N4 k! O6 I3 q- J# {; [8 w* b" X! O
  1. if getCount(st) >= 4 :
    ; e" x9 g; Y3 p. U" V/ J8 m
  2. completed(st), c% e  \' C" N; l5 y
  3.   return
    ' v' t2 \4 p' ?
  4. ( B$ S. U: @/ t1 {& F5 _, [
  5. class Quest (JQuest):
    , P: b8 U( q5 b3 K- [3 j
  6.   def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)
    , L2 C. l2 M6 A' s' q* {1 C
  7.   def onEvent (self,event,st):7 x- o5 {2 Q1 c8 ]5 }
  8. id = st.getState()5 `: q6 u. U7 `
  9. if   id == CREATED  : st.setState(STARTED)
    / j' P; p* I( ^9 T' L
  10. elif id == COMPLETED: pass
    , B: C$ `7 Q& X6 M1 ~  e
  11. elif id == STARTED  : check(st)
    " W; \! P, U  f( z- b, k
  12. return+ a6 r+ }* j* z' G: R. S4 M

  13. . R0 I2 p9 A1 W+ ?1 d& T
  14. QUEST     = Quest(201,"Tutorial", "Tutorial quest")
    $ E1 \- s" s; k$ _2 ^  [5 v8 k
  15. CREATED   = State('Start',     QUEST)
    % e; n) }: T+ b2 M
  16. STARTED   = State('Started',   QUEST)) p% ~' q2 j- T0 R0 [
  17. COMPLETED = State('Completed', QUEST)) j0 z& t7 Q  L- z' e7 Z8 X& a5 f
  18. * B3 V8 p" V$ n
  19. QUEST.setInitialState(CREATED)$ f: h9 d9 ^. p* B, D9 H
  20. QUEST.addStartNpc(7056)
    1 O0 x$ F  K* F! G2 ]
  21. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Теперь рассмотрим, как это работает.: G3 _( \& ^: x' z
Игрок подходит к начальному NCP (в данном случае 7056), нажимает на «Quest». Квест будет создан и состояние квеста перейдет к CREATED и игроку будет показана страничка Start.htm с описанием квеста. Тогда метод onEvent, поле открытия странички Start.htm переведет состояние квеста в STARTED и игроку будет показана страничка Started.htm, где будет опсание того, как найти keltirs и .т.д.9 m. D& R5 c) P$ {
, z9 P( {3 j! i# i  i% @0 O

% r% R' ~/ }) e3 wПри состоянии STARTED будет зарегистрирован дроп «fangs» при убийстве keltirs. Игрок может вернуться к стартовому NCP и спросить о квесте – метод onEvent будет вызван снова. Если у игрока не хватает необходимого количества предметов, то метод check() не переведет квест в следующее состояние и Started.htm будет показана снова. Но если игрок собрал необходимее количество предметов (в данном случае 4 клыка), то метод check() вызовет метод completed() который переведет квест в новое состояние COMPLETED, заберет все клыки, даст карту мира, т.к. это награда за квест, покажет Completed.htm и завершит квест.
7 i  k2 n1 V. Y) R
: M. L. @( }# q/ A, k  T% ?

" ^% \/ l  O! B! U* T: S2 X9 @Теперь давайте сделаем наш квест более похожим на то, что он должен из себя представлять.+ |" e; _( H- `- t
Прежде всего у нас есть 3 метода для объявления их в Яве – onTalk, onKill и onEvent. Если методы onTalk и onKill не объявлены, то за них все будет делать метод onEvent, т.е. определять квестовых монстров и вызывать диалоги NCP. Есть примечание, методы onTalk и onKill будут вызывать только диалоги с NCP в зависимости от текущего состояния квеста. Метод onKill будет вызываться только тогда, когда мы убиваем квестового монстра.3 H6 ]! i% \! _3 L8 q

8 x' H7 i" b! f" N7 ^! l

" a1 x; h2 E; o0 b6 DДавайте рассмотрим как вызывается метод onKill при убийстве keltir в состоянии квеста STARTED:
( D8 l& l$ Z6 E- Y: r; \
  1. STARTED.addKillId(KELTIR_NPC_ID)
Скопировать код
и метод onKill в классе Quest:' m; ^9 f/ @8 ]
8 S- ^1 m6 r- _
  1. class Quest (JQuest):" C1 j& Z$ }1 {+ o8 S" f% R; }9 k
  2.   ...4 K, ?1 u- K& {, V+ h9 j
  3.   def onKill (self,npcId,st):$ j& j- U# [; M6 E6 m6 l5 |4 V; |
  4. if npcId == KELTIR_NPC_ID:- {  ^* `) f$ Y4 t/ C
  5.    n = getCount(st)
    0 \( D& w7 e' b  b( r
  6.    if   n == 0:1 f: m# i" J2 a9 D( S: d
  7.        return "Chat0.htm"/ h  u$ f1 a0 o% z# I% c- E
  8.    elif n == 1:
    + f; z) d( L% K- T& h
  9.        return "Chat1.htm") t, F' ^" w* g) o
  10.    elif n >= 4:
    # c6 \8 W' P; m7 \
  11.        return "Chat4.htm"4 j3 ]5 Y+ v- F$ v0 C' L
  12.    return "Collected "+str(n)+" of 4 fangs"* ?, t6 e: U; ?3 q: w0 e
  13. return
Скопировать код
Метод onKill (а так же метод onTalk) имеет следующие параметры:5 B; F! c' Y7 ~8 [6 V3 e

* d& u' }0 H- q4 z

! Z( t! O% q6 u0 H3 P3 d! b• self – квест7 }0 Y; ]5 Y. E3 ]0 u& l0 g
• npcId – ID NCP, которого мы должны убивать (если это метод onTalk, то ID того NCP, с которым мы должны поговорить).
! ]2 m# B: O+ I0 C/ \$ ]• st – текущее состояние игрока.9 l( E) @3 P' ?$ e% {) k, i& K

8 L/ L. S) P9 k* c0 h% \9 ^7 u

. J! o2 b1 F0 [5 @) }  NВ этом методе мы проверяем и отмечаем, является ли убитый NCP keltir’ом. В основном эта проверка не нужна, т.к. у нас только KELTIR_NPC_ID./ c0 M/ r  _' T! V" s
4 x( @3 o! |3 I' I' Z9 P4 y) Z  L. z

- p0 u0 k. r: Q6 @7 e9 R$ I8 g) V; C" HЗатем проверяем количество предметов (в данном случае количество клыков), и если их вообще нет, то возвращаем строку "Chat0.htm", если только один предмет, то возвращаем строку "Chat1.htm", если же предметов 4 или больше, то "Chat4.htm". Если строка возвращена из методов onEvent, onKill или onTalk, то сервер покажет соответствующие htm. В Chat0.htm может иметь следующий текст: «Вы не имеете ни одного клыка, возвращайтесь позже, когда соберете 4 штуки и бла, бла, бла…», в Chat1.htm может быть следующий текст: «У Вас всего 1 клык, по этому соберите еще…». В Chat4.htm – «Вы собрали необходимое количество предметов, возвращайтесь к вашему тренеру, что бы завершить квест…»
8 \* n- l& G. U" W+ u& h" i' d
1 @$ x: @6 e- _, ?& ?

$ m( H8 V+ R0 s6 e4 JПримечание: если в строка return начинается с "<html>", то будет показана страничка html с текстом, который стоит далее. Так вместо:! Y3 j  q5 e. Y5 h: W
return "Chat4.htm"* J/ `% W6 l4 i* x6 c% R3 K

7 B) p/ \# |2 S# Z( e4 S( w
5 y7 i* ^  i% m7 s  t0 w
можно поставить:
0 O1 k. C2 H- Y! p
  1. return "<html><body>Return to your trainer to complete the quest</body></html>"
Скопировать код
Так же если строка заканчивается без .htm или в начале нет <html>, то текст будет выведен, как системное сообщение в окне чата. В нашем случае мы сделаем так, что бы при каждом убийстве keltik выводилось системное сообщение: «Собрано N из 4-х клыков».+ q5 J1 _$ D1 W$ ~  d! G
) r5 ^/ V2 h4 ^, f( M
% N8 K! p/ |3 s1 L7 x
Наш код для onKill имеет один недостаток. Он будет постоянно показывать Chat0.htm, Chat1.htm и Chat4.htm, нам же необходимо, что бы Chat0.htm и Chat1.htm показывались только один раз. Как нам это сделать? С помощью переменных.
4 K; d1 i  t% P; u" ]% E& F# ]1 u3 c$ r- c# w% t
9 ~0 B! W1 `" s8 H
В каждом квесте строки могут храниться с помощью переменных. Эти переменные сохраняются в Вашей БД. В каждом методе мы можем назначить, прочитать и удалить переменные. Давайте изменим метод onKill, так что бы каждый диалог вызывался только один раз.- t# v$ e" T5 b/ e3 E0 ^
  1. def onKill (self,npcId,st):. F3 E$ g9 i8 g: x
  2. if npcId == KELTIR_NPC_ID:
    % q5 Q6 ~4 y2 n5 Z- X
  3. n = getCount(st)
    & z# ?1 }4 c0 F! e% k4 f5 J
  4. if n == 0:
    - n- d/ ?6 e7 |+ w* T
  5. if st.get('chat0') == None :; `' j" j, K( J( I- e
  6. st.set("chat0", "true")1 d3 ^' F' A  J5 j( I; @& y! }) N
  7. return "Chat0.htm"
    ! y8 A. Z1 }  d: a
  8. elif n == 1:
    ; `7 F! \" y/ U- ~3 p
  9. if st.get('chat1') == None :
    0 i0 y7 }6 c+ x' b6 H# K6 A
  10. st.set("chat1", "true")! b3 ^8 z0 f& v9 t- C% a8 J, |
  11. return "Chat1.htm"
    ' i" Y8 ]. O9 u7 I  x- f
  12. elif n >= 4:
    " L( I# Q8 H6 H% h) `7 u
  13. return "Chat4.htm"$ n: j/ x' \7 k5 T( @9 t( s. s( V
  14. return "Collected "+str(n)+" of 4 fangs"
    / P& U! E" W* \5 Y3 d# q# k
  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.
, r, C& y) W7 ]( ]7 r; r! d' Y# @' X! ~! V. C; w% Y9 b) H/ S; N5 Z

: z+ l; \; C6 f& k- Z. _Вот конечный рабочий вариант квеста:5 s3 v. q7 J7 V& \
  1. import sys
    , U6 [* e7 l+ S- ?1 ]
  2. from net.sf.l2j.gameserver.model.quest import State
    % f3 {, f& p9 A
  3. from net.sf.l2j.gameserver.model.quest import QuestState
    : t; `: K" N2 `5 t  G9 j. }! B
  4. from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest
    4 s3 [7 z+ _+ |9 N" I8 d
  5. ( N/ L4 s; A8 R
  6. KELTIR_NPC_ID = 12082
    ! E# ], H( |% F
  7. FANGS_ITEM_ID = 1859% ~9 j! P7 s# A6 @" r0 }2 W
  8. DROP_RATE    = 500000
    7 T9 G; H. \- N$ ^

  9. - c$ X7 J! r* N5 Y  i7 w4 X
  10. WORLD_MAP_ITEM_ID = 1665
    . y1 r+ Q% v% h' Z; _$ |

  11. $ Z1 N  q- i1 s! w( z# w% \
  12. def getCount(st) :. ?% i; U+ i# ^7 J4 j
  13. return st.getQuestItemsCount(FANGS_ITEM_ID)% ]# ^: b* V% K. T. f
  14. # L* x1 I7 Y. Q
  15. def completed(st) :
    ) _/ ^: b+ e; r3 b: w3 q' y
  16. st.setState(COMPLETED)- a$ Z0 j5 U0 x% K' b0 W' |) }
  17. st.clearQuestDrops()# L$ m4 i: E9 Y$ C) C: ]
  18. st.takeItems(FANGS_ITEM_ID,-1)
    . v9 a8 B! g3 c7 V6 @
  19. st.giveItems(WORLD_MAP_ITEM_ID,1)0 W; w: }! ~% b+ y
  20. st.exitQuest(False)
    5 s) }# j4 x% e+ G
  21. return  ~! W% p  n+ _1 m3 V) z

  22. ; Y1 ^3 X  g3 {6 D0 h
  23. def check(st) :& y8 Y2 s( e& z" \4 U" c& ?0 C
  24. if getCount(st) >= 4 :7 ]' B$ K3 T7 e
  25. completed(st)
    * [$ B2 r6 o6 [. D1 K
  26. return
    % Y+ @# H# Q6 y9 x4 B6 B! ~

  27. 1 T# r, w) c" F' N8 W3 j
  28. class Quest (JQuest):
    / t: v" Z7 c5 z( m
  29. 1 o: A2 v6 O* c# y
  30. def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)/ ~! x& r4 X: @/ O& v

  31. 6 q- A: u+ m' ^- M
  32. def onEvent (self,event,st):
      d! p( k5 N/ C
  33. id = st.getState()# r$ X2 }2 k  ]9 X, }/ C
  34. if id == CREATED : st.setState(STARTED)
    + x2 R% l* e3 v5 ?6 z. H
  35. elif id == COMPLETED: pass+ K1 U8 v4 T+ B7 Z7 ^1 B* D
  36. elif id == STARTED : check(st)
    $ h3 Z2 B+ ^5 n# \9 s
  37. return
    ' [9 T9 {8 f) B5 n2 ]

  38. ' O- ^8 l+ `8 q7 F1 c: g6 l
  39. def onKill (self,npcId,st):
    6 h; R& o  t) a2 l
  40. if npcId == KELTIR_NPC_ID:
    6 |3 l( n5 P4 A) E# S, F  P  m# Q
  41. n = getCount(st)7 \& A3 B) C/ F. ]9 R1 E7 X6 N
  42. if n == 0:
    + w6 I, G  L1 Y" p0 @
  43. if st.get('chat0') == None :# `; Q' Z6 d9 j: i0 s  L  N
  44. st.set("chat0", "true")
    5 m& p3 t- U) {1 D
  45. return "Chat0.htm"  M! a' [5 [6 s" n" J3 p
  46. elif n == 1:
    ! i; |$ s2 v+ ]! L# ?
  47. if st.get('chat1') == None :: e! M: `9 b- L
  48. st.set("chat1", "true")/ k8 D& P. p2 r4 H7 k& @# q
  49. return "Chat1.htm"
      p9 s  t: Z- o* T( s
  50. elif n >= 4:
    2 O0 Z/ h) E: y, M  [
  51. return "Chat4.htm". l1 q) [$ `! P, }% v
  52. return "Collect "+str(n)+" of 4 fangs"+ w/ D3 m. c4 _! Q* u! C
  53. return
    4 [* A9 x- w. f
  54. . N* B; _; n. n& u2 {
  55. QUEST    = Quest(201, "Tutorial", "Tutorial quest")
    1 {% Z7 n+ b8 U5 V" [. `
  56. CREATED = State('Start',    QUEST)
    3 u4 L( O- r, P3 x4 q
  57. STARTED = State('Started', QUEST)+ ]- C, [7 A" J) ^" U7 f
  58. COMPLETED = State('Completed', QUEST)
    3 U' \! a( w, ^* o
  59. ; G7 i. U* v  I2 E2 j) m0 p  l
  60. QUEST.setInitialState(CREATED)
    3 e( L' C0 b4 i" _  v
  61. QUEST.addStartNpc(7056)
    ( n7 p. L' \4 C6 d& Y- m+ ^) L
  62. QUEST.addStartNpc(7012)% O$ ~, W: C- O1 N6 Y; t9 z( I
  63. QUEST.addStartNpc(7009)+ d, w( z/ J) }1 ]5 h% n' r
  64. QUEST.addStartNpc(7011)
    ; c: J' K- M3 G

  65. ( \5 u5 c9 W. q8 d  I7 k3 S/ N
  66. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
    ( Q; R( t0 x! X1 X
  67. STARTED.addKillId(KELTIR_NPC_ID)/ C1 K9 l( F8 X9 q4 a8 h7 v
  68. STARTED.addTalkId(7056)4 l! d' F8 V3 U" [' X* o: q
  69. STARTED.addTalkI d(7012)& j+ l# w0 T7 T5 m+ d% U# N
  70. STARTED.addTalkId(7009)/ z0 g! p! T/ _8 l' S# G3 @
  71. STARTED.addTalkId(7011)
Скопировать код
& x- K4 h; C' d% K; _, q4 c

7 d. C$ g0 P, U7 M/ U




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