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

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

Автор: admin    Время: 2024-9-17 19:36
Название: Создаём собственные квесты (часть 2)
Хроники        Interlude- s9 y) [+ P/ S. P2 L  ^" u$ I
Сборка        L2j
# q7 i+ T% g) o4 iОбратите внимание: в файле htm стартового NCP обязательно должна быть ссылка на квест:
/ k, P7 f' S( }
  1. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Для этого квеста больше ничего не надо. Все необходимое для корректной работы квеста уже добавили. Вот полный текст квеста:
9 ?( ^8 o" x7 W: R; m& j) Q6 x4 Z5 H' A# p* m7 a
  1. if getCount(st) >= 4 :1 ~' b) g  D( y8 g; S
  2. completed(st)& c0 K" |" J4 ]6 ~- l  S
  3.   return3 Z. i4 u4 F9 \9 O2 Q' E
  4. + P# y; r! C# M
  5. class Quest (JQuest):* d. G! J6 T& @5 {" q3 F0 H; {
  6.   def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)
    ; j7 P" L  A" }3 p# t( n
  7.   def onEvent (self,event,st):1 g4 W: w  z% v( Z
  8. id = st.getState()# n& m, l9 Y; n
  9. if   id == CREATED  : st.setState(STARTED)7 S# f8 P; A6 E. y% H+ n
  10. elif id == COMPLETED: pass
    $ S( _$ W, ~$ i  W$ S
  11. elif id == STARTED  : check(st)+ x9 [# r) g4 H
  12. return" J- g1 v! t  e  s* |8 k; q  v

  13. ; I6 X8 c$ I, Z. T5 Y" x& x7 t* J7 `) Q
  14. QUEST     = Quest(201,"Tutorial", "Tutorial quest")
    & S  ?$ m- @- W; i" B
  15. CREATED   = State('Start',     QUEST)
    ) o+ Q) J$ G: x3 m- x8 f8 H
  16. STARTED   = State('Started',   QUEST)# }; A2 k9 b7 }$ P
  17. COMPLETED = State('Completed', QUEST)
    6 }# K( _0 Q$ v# H; f9 {
  18. # G9 z- F  H- A+ w
  19. QUEST.setInitialState(CREATED)( R3 V& n6 K% f# M
  20. QUEST.addStartNpc(7056)
    7 l+ H, k! R* t3 d
  21. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)
Скопировать код
Теперь рассмотрим, как это работает.
) W7 @: j6 [) `2 |4 I. UИгрок подходит к начальному NCP (в данном случае 7056), нажимает на «Quest». Квест будет создан и состояние квеста перейдет к CREATED и игроку будет показана страничка Start.htm с описанием квеста. Тогда метод onEvent, поле открытия странички Start.htm переведет состояние квеста в STARTED и игроку будет показана страничка Started.htm, где будет опсание того, как найти keltirs и .т.д.
( o) E# B* P" ~2 h" P
6 i0 O8 k; U4 Z( e. F5 X

. h0 o6 \1 p5 ?При состоянии STARTED будет зарегистрирован дроп «fangs» при убийстве keltirs. Игрок может вернуться к стартовому NCP и спросить о квесте – метод onEvent будет вызван снова. Если у игрока не хватает необходимого количества предметов, то метод check() не переведет квест в следующее состояние и Started.htm будет показана снова. Но если игрок собрал необходимее количество предметов (в данном случае 4 клыка), то метод check() вызовет метод completed() который переведет квест в новое состояние COMPLETED, заберет все клыки, даст карту мира, т.к. это награда за квест, покажет Completed.htm и завершит квест.' g( p+ ]) s( |4 H# n- @" a

9 S+ r7 U0 a% }4 o2 e

9 I. M+ M% m* f9 v# ZТеперь давайте сделаем наш квест более похожим на то, что он должен из себя представлять.# g) `: R& q6 h1 y, d  j; F
Прежде всего у нас есть 3 метода для объявления их в Яве – onTalk, onKill и onEvent. Если методы onTalk и onKill не объявлены, то за них все будет делать метод onEvent, т.е. определять квестовых монстров и вызывать диалоги NCP. Есть примечание, методы onTalk и onKill будут вызывать только диалоги с NCP в зависимости от текущего состояния квеста. Метод onKill будет вызываться только тогда, когда мы убиваем квестового монстра.
! z7 d) H7 |  J$ A4 a8 p5 a" l7 \( ~% v" {+ b0 Q# L$ @* Y. z$ H+ r

; u$ x* l$ P/ k: m  M6 f4 g3 PДавайте рассмотрим как вызывается метод onKill при убийстве keltir в состоянии квеста STARTED:
6 P8 Q4 h! ^; D5 T2 x' A! B
  1. STARTED.addKillId(KELTIR_NPC_ID)
Скопировать код
и метод onKill в классе Quest:
, [; Z# ]& P5 \; m% J. l. M4 m5 O# Z+ v# h
& k2 l9 u3 r: Z$ m3 l
  1. class Quest (JQuest):6 ^  |! S; t$ z/ [" _$ w
  2.   ...
    4 t( S& u" v3 ?4 V: O
  3.   def onKill (self,npcId,st):
    5 c& p$ t( ^5 R- v5 ^0 {* Q4 {
  4. if npcId == KELTIR_NPC_ID:% j  P- d/ g9 Q" F% v8 K% @, y
  5.    n = getCount(st)1 ]2 b$ V* K+ F1 Y9 O$ E
  6.    if   n == 0:: B; U' D3 Z4 d, V% D* j- c! b
  7.        return "Chat0.htm"
    7 B. j  E  f+ h6 j( E5 _1 h' ?: X+ w
  8.    elif n == 1:4 I4 i7 y( ^2 {8 z, B! b* U
  9.        return "Chat1.htm". E$ b2 q  Y) z( ^! ]
  10.    elif n >= 4:# R, {* g! u( ]& V2 m6 V7 Y( T
  11.        return "Chat4.htm"2 Z: F3 n" p4 b5 x+ ~: x$ m
  12.    return "Collected "+str(n)+" of 4 fangs"9 m) V( }8 R* v! ~7 J6 r- k+ j
  13. return
Скопировать код
Метод onKill (а так же метод onTalk) имеет следующие параметры:4 K% A4 M6 u" y& q

# D* r8 T4 k5 k3 }; @5 ~1 c
8 h' ^7 j  Y! Z% W. ~
• self – квест
6 R2 J: x2 T: L& d7 q/ s/ N• npcId – ID NCP, которого мы должны убивать (если это метод onTalk, то ID того NCP, с которым мы должны поговорить).6 Y. @3 z8 @8 N$ [
• st – текущее состояние игрока.
3 W" \& ~- N) G! V( `5 p* U* R  K; n; A5 L7 S2 q" c# ]+ ?) E! j
$ j4 i- t) |( d6 G; M+ j
В этом методе мы проверяем и отмечаем, является ли убитый NCP keltir’ом. В основном эта проверка не нужна, т.к. у нас только KELTIR_NPC_ID.
% j) p. u# A  @4 b+ o# c9 z' r, t, ^
5 |9 X6 G& r+ H8 e0 s! [
Затем проверяем количество предметов (в данном случае количество клыков), и если их вообще нет, то возвращаем строку "Chat0.htm", если только один предмет, то возвращаем строку "Chat1.htm", если же предметов 4 или больше, то "Chat4.htm". Если строка возвращена из методов onEvent, onKill или onTalk, то сервер покажет соответствующие htm. В Chat0.htm может иметь следующий текст: «Вы не имеете ни одного клыка, возвращайтесь позже, когда соберете 4 штуки и бла, бла, бла…», в Chat1.htm может быть следующий текст: «У Вас всего 1 клык, по этому соберите еще…». В Chat4.htm – «Вы собрали необходимое количество предметов, возвращайтесь к вашему тренеру, что бы завершить квест…»2 \8 M, u% \% Z% r! w) c
+ }7 E. J2 h2 C% @: |- z4 a/ R
# S$ z! H0 o! [% {" ]- a! ?5 n
Примечание: если в строка return начинается с "<html>", то будет показана страничка html с текстом, который стоит далее. Так вместо:6 b) u% v4 o' S) Z, }! b9 f; E, E1 \- J
return "Chat4.htm"
& C% u1 Y0 u8 {% X' d; g+ Y) y+ Z" m0 J' e
2 [- j$ M& T4 s( A$ B1 j8 ]7 A( t
можно поставить:4 g' U: N: w* l) R& ?3 O8 _
  1. return "<html><body>Return to your trainer to complete the quest</body></html>"
Скопировать код
Так же если строка заканчивается без .htm или в начале нет <html>, то текст будет выведен, как системное сообщение в окне чата. В нашем случае мы сделаем так, что бы при каждом убийстве keltik выводилось системное сообщение: «Собрано N из 4-х клыков».
$ r9 P7 y" T4 S: T' K
6 @9 ]/ Z0 B# j9 a7 t" e+ g8 N

8 y  t3 t2 m2 B4 d+ H* rНаш код для onKill имеет один недостаток. Он будет постоянно показывать Chat0.htm, Chat1.htm и Chat4.htm, нам же необходимо, что бы Chat0.htm и Chat1.htm показывались только один раз. Как нам это сделать? С помощью переменных.
! \2 E$ i8 h! }  L* w% Q; |# J; ]3 L8 |+ C& p% k  p" ]5 U" k7 t
5 ~3 |7 E; Z% I- P, M/ f9 z
В каждом квесте строки могут храниться с помощью переменных. Эти переменные сохраняются в Вашей БД. В каждом методе мы можем назначить, прочитать и удалить переменные. Давайте изменим метод onKill, так что бы каждый диалог вызывался только один раз.5 D( ^7 |* s! d3 s% ^1 W. M+ h5 F1 Y
  1. def onKill (self,npcId,st):% T( D! |) H- q2 p1 M( U; }9 U
  2. if npcId == KELTIR_NPC_ID:
    0 P5 [9 J  {7 F( g7 j- n8 T. J# C
  3. n = getCount(st)
    & i9 E2 f. s- [1 z* B
  4. if n == 0:9 T# s3 S* y* J' U8 s) v
  5. if st.get('chat0') == None :
    1 g- H/ l) d! F) W0 g
  6. st.set("chat0", "true")% h& t5 H+ R& _$ f1 s2 p' p
  7. return "Chat0.htm"
    , r& V: d* z' ?6 L' a0 U
  8. elif n == 1:/ l: f2 r1 `6 r' [& \& Z
  9. if st.get('chat1') == None :( w* K( q5 ]! ~( ~( ?
  10. st.set("chat1", "true")
    4 i; u+ f" p+ h. h6 _" a9 \* h
  11. return "Chat1.htm"( B! }. j" T, N
  12. elif n >= 4:5 I) ~  |# H6 w) _% Y. R% _! ^
  13. return "Chat4.htm"
    * C( f6 `; A$ F0 P# e7 ?
  14. return "Collected "+str(n)+" of 4 fangs"
    $ J0 T" {( B6 h7 @
  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.) a' u5 c7 X7 V1 s, h

) e9 m" }6 h+ L6 ]
% p* V' H" o0 a- \
Вот конечный рабочий вариант квеста:6 J1 K' }/ g  J1 I
  1. import sys" W0 `3 _# F; s3 \+ K% b: C7 Z! |4 d# M6 k
  2. from net.sf.l2j.gameserver.model.quest import State" B; [2 c+ t3 ?' W
  3. from net.sf.l2j.gameserver.model.quest import QuestState! U' w! W+ p! K
  4. from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest. d% [) G( X8 Q& _
  5. 4 A: {/ f3 @: X
  6. KELTIR_NPC_ID = 120823 M, u: L% W. V4 h" u& `
  7. FANGS_ITEM_ID = 1859
    . G9 w  j  _- M1 X- v0 ^! N
  8. DROP_RATE    = 500000# z' T- R8 C$ n3 E8 J
  9. % g9 @3 g2 w2 s$ j; k. @
  10. WORLD_MAP_ITEM_ID = 1665. d: U) z, G5 |7 m

  11. 0 [' j/ D! o+ p
  12. def getCount(st) :9 Y) x0 i0 L9 W
  13. return st.getQuestItemsCount(FANGS_ITEM_ID)# J9 U1 P! p; H9 Q: ~0 u* h; v" m% X
  14. * @% e+ ^4 \3 D. E
  15. def completed(st) :7 B/ R. j" c6 l* d% {
  16. st.setState(COMPLETED)
    4 G+ l8 ~  Y9 b$ O. I6 b0 z4 @
  17. st.clearQuestDrops()0 l( c& Z' x+ j2 U" ^& u! J1 |! g
  18. st.takeItems(FANGS_ITEM_ID,-1)- e6 f# u6 {& w, b3 g1 g/ z
  19. st.giveItems(WORLD_MAP_ITEM_ID,1)
    8 J' Q4 i2 X( o' P1 H
  20. st.exitQuest(False)
    * S9 e4 ^/ h4 _
  21. return" u* l8 f* l6 i% ], w
  22. 0 j7 O0 ^& F4 ~
  23. def check(st) :7 R0 ?# B. J; M; f6 L4 |
  24. if getCount(st) >= 4 :
    7 [6 D: Q) q+ ^. i9 l
  25. completed(st). ~2 Y3 ], q8 l! A( W: s
  26. return
    % ^$ B8 J5 r# r- d& ?4 d

  27. 7 k+ U8 N6 t" j" D& H$ A7 f3 C
  28. class Quest (JQuest):4 X7 O" B7 F& a/ M

  29. ' L& S& ]* b* N2 F! V0 T
  30. def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)
    " K+ H7 l5 B: d& k; [8 O% l
  31. 6 B% t5 ?. ]1 I( C; ?  R
  32. def onEvent (self,event,st):
    ) g* ?! e! M0 _. f. R  f
  33. id = st.getState()$ z: _3 ]* n1 _% z0 v/ j4 k6 U, H
  34. if id == CREATED : st.setState(STARTED)
    . d& G& ~) J, _( c
  35. elif id == COMPLETED: pass
    6 J) r# y; J) S8 p6 U# S! X
  36. elif id == STARTED : check(st)
    4 r( z! u, n$ Z
  37. return
    # z# {' q0 N& ~

  38. - {8 A/ b$ K! Q, N7 _
  39. def onKill (self,npcId,st):, b$ @; m9 G5 W& t5 J% z" I2 l
  40. if npcId == KELTIR_NPC_ID:
    " W1 _; s5 F9 {; V+ f
  41. n = getCount(st)+ q* n. g9 ~! b' f/ M# }
  42. if n == 0:# `2 i( o) Q; [( `
  43. if st.get('chat0') == None :- \; g% V3 n, ]& [4 K
  44. st.set("chat0", "true")
    5 F6 `* @8 h9 }- w: M/ E
  45. return "Chat0.htm"* x: d) A* d3 E. o- h' q
  46. elif n == 1:: u% [- l# t' y: w' G+ `2 v
  47. if st.get('chat1') == None :
    . S5 Q( M2 R# u9 ]8 `- E
  48. st.set("chat1", "true")6 m, t# `  n! q) W- w
  49. return "Chat1.htm"
    % }, f3 [! S2 G$ G, u5 k: w
  50. elif n >= 4:: w0 j' W/ b2 [+ t$ B- ^$ [
  51. return "Chat4.htm"
    $ N* A9 t2 D8 k, ~9 L4 K# J, P
  52. return "Collect "+str(n)+" of 4 fangs"3 I4 X0 Z3 s, ]
  53. return
    8 `1 n# ]( j1 h) T6 M. V; [) c
  54. * U7 L2 E, W0 h8 H, k: t0 F. v& p- M
  55. QUEST    = Quest(201, "Tutorial", "Tutorial quest")
    2 ]8 L" A$ r) S8 |
  56. CREATED = State('Start',    QUEST)! y! _. j3 k( A
  57. STARTED = State('Started', QUEST)
    + w, m& ~# i0 f) Y. m
  58. COMPLETED = State('Completed', QUEST), x% s$ b/ i( |6 ~
  59. 6 Z4 k( J* r& V& \$ k! F
  60. QUEST.setInitialState(CREATED)9 f% x/ n/ @, u
  61. QUEST.addStartNpc(7056)* @% Y9 U2 a* l' r& p3 g  e
  62. QUEST.addStartNpc(7012)8 z& d$ `  n8 v( Y" U7 Y
  63. QUEST.addStartNpc(7009)( o- h! a4 n6 X/ J: D- H
  64. QUEST.addStartNpc(7011)
    / B" f# r/ e& Y5 |5 P
  65.   y" q: t8 j0 b9 {( v
  66. STARTED.addQuestDrop(KELTIR_NPC_ID,FANGS_ITEM_ID,DROP_RATE)$ t; [8 r' q5 o( _2 S
  67. STARTED.addKillId(KELTIR_NPC_ID)
    # B+ o: ]7 I. U6 \$ ~5 v
  68. STARTED.addTalkId(7056)7 h1 o2 t; f0 M- [( U/ V7 W* K
  69. STARTED.addTalkI d(7012)8 }" }" P$ N4 a' j! ^6 E
  70. STARTED.addTalkId(7009)
    4 @) @( L& a* R6 A
  71. STARTED.addTalkId(7011)
Скопировать код

9 T7 T5 Q7 S) E3 |+ n" K7 s2 q" h& {: F7 F$ ?2 O: @8 o$ x





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